package com.jrx.anytxn.transaction.service.impl;

import com.jrx.anytxn.common.constant.CommonContants;
import com.jrx.anytxn.common.data.TxnRespCode;
import com.jrx.anytxn.common.exception.TxnBizException;
import com.jrx.anytxn.common.uitl.DateUtils;
import com.jrx.anytxn.common.uitl.StringUtils;
import com.jrx.anytxn.customer.constant.CustomerConstant;
import com.jrx.anytxn.customer.entity.CmCustomerFirstLevel;
import com.jrx.anytxn.customer.entity.CmCustomerLimitInfo;
import com.jrx.anytxn.customer.entity.CmCustomerRightInfo;
import com.jrx.anytxn.customer.entity.CmCustomerSecondLevel;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerFirstLevelMapper;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerSecondLevelMapper;
import com.jrx.anytxn.customer.service.ICustomerLimitInfoService;
import com.jrx.anytxn.number.exception.SegmentException;
import com.jrx.anytxn.number.service.ISegmentService;
import com.jrx.anytxn.param.entity.PrAmortMethod;
import com.jrx.anytxn.param.entity.PrInstallmentFeeTable;
import com.jrx.anytxn.param.entity.PrInterestTable;
import com.jrx.anytxn.param.entity.PrProductTable;
import com.jrx.anytxn.param.entity.PrPromotionInfo;
import com.jrx.anytxn.param.entity.PrWaiveInterestTable;
import com.jrx.anytxn.param.enums.BizTypeEnum;
import com.jrx.anytxn.param.service.product.IAmortMethodService;
import com.jrx.anytxn.param.service.product.IInstallmentFeeTableService;
import com.jrx.anytxn.param.service.product.IInterestTableService;
import com.jrx.anytxn.param.service.product.IPrWaiveInterestTableService;
import com.jrx.anytxn.param.service.rights.IPrPromotionInfoService;
import com.jrx.anytxn.param.utils.BizTypeProducts;
import com.jrx.anytxn.transaction.bean.LoanParamBean;
import com.jrx.anytxn.transaction.bean.LoanPlanParamBean;
import com.jrx.anytxn.transaction.bean.PromotionLabelBean;
import com.jrx.anytxn.transaction.constant.TransBizConstant;
import com.jrx.anytxn.transaction.dto.common.CommonAddReq;
import com.jrx.anytxn.transaction.dto.loan.CustomerRenewalRes;
import com.jrx.anytxn.transaction.dto.loan.LoanReq;
import com.jrx.anytxn.transaction.entity.AmLoanMainInfo;
import com.jrx.anytxn.transaction.entity.AmRepaymentPlanInfo;
import com.jrx.anytxn.transaction.entity.AmWaiveRepaymentPlanInfo;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmRepaymentPlanInfoMapper;
import com.jrx.anytxn.transaction.service.IPromotionService;
import com.jrx.anytxn.transaction.service.IRepaymentPlanService;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 还款计划service实现
 *
 * @author : zhenghaopu
 * 2019/10/21
 */
@Service
public class RepaymentPlanServiceImpl implements IRepaymentPlanService {


    private static final Logger logger = LoggerFactory.getLogger(RepaymentPlanServiceImpl.class);

    @Resource
    private ExtAmRepaymentPlanInfoMapper extAmRepaymentPlanInfoMapper;
    @Autowired
    private IInstallmentFeeTableService installmentFeeTableService;
    @Autowired
    private IInterestTableService interestTableService;
    @Autowired
    private IPrWaiveInterestTableService waiveInterestTableService;
    @Autowired
    private IAmortMethodService amortMethodService;
    @Autowired
    private ISegmentService segmentService;
    @Autowired
    private IPromotionService promotionService;
    @Autowired
    private IPrPromotionInfoService prPromotionInfoService;
    @Autowired
    private ExtCmCustomerSecondLevelMapper extCmCustomerSecondLevelMapper;
    @Autowired
    private ExtCmCustomerFirstLevelMapper extCmCustomerFirstLevelMapper;
    @Autowired
    private ICustomerLimitInfoService iCustomerLimitInfoService;

    @Override
    public List<AmRepaymentPlanInfo> getPlanList(String loanId, String customerId, String tenantId,
                                                 List<String> statusList) throws TxnBizException {
        //查询订单还款计划
        List<AmRepaymentPlanInfo> planInfoList = extAmRepaymentPlanInfoMapper.selectByLoanIdAndStatus(loanId
                , statusList, customerId, tenantId);
        //是否有还款计划
        if (CollectionUtils.isEmpty(planInfoList)) {
            logger.error("未查询到订单号：{}对应的还款计划", loanId);
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }
        return planInfoList;
    }

    /**
     * 构建还款计划入参
     *
     * @param amLoanMainInfo  订单信息
     * @param businessDate    业务日期
     * @param loanAmt         贷款金额
     * @param tenor           期数
     * @param loanParamBean   贷款参数
     * @param firstPaymentDay 首期还款日
     * @return 还款计划入参
     * @throws TxnBizException 自定义异常 没有利率等配置时抛出异常
     */
    @Override
    public LoanPlanParamBean buildLoanPlanParamBean(AmLoanMainInfo amLoanMainInfo, Date businessDate,
                                                    BigDecimal loanAmt, Integer tenor, LoanParamBean loanParamBean, Date firstPaymentDay) throws TxnBizException {
        LoanPlanParamBean loanPlanParamBean = new LoanPlanParamBean();
        if (null != loanParamBean) {
            loanPlanParamBean.setInterestTable(loanParamBean.getInterestTable());//利息参数
            loanPlanParamBean.setPrProductTable(loanParamBean.getProduct());
            loanPlanParamBean.setPrWaiveInterestTable(loanParamBean.getPrWaiveInterestTable());
            PrProductTable prProductTable = loanParamBean.getProduct();
            loanPlanParamBean.setMinIntervalDays(prProductTable.getStatemntInterval());//最小间隔天数
            loanPlanParamBean.setRightInfo(loanParamBean.getRightInfo());
        }

        PrInstallmentFeeTable installmentFee =
                installmentFeeTableService.findInstallmentByFeeId(amLoanMainInfo.getFeeTableId(),
                        amLoanMainInfo.getOrganizationId(), amLoanMainInfo.getTenantId());

        PrAmortMethod amortMethod = amortMethodService.findByChargerOptionAndId(amLoanMainInfo.getAmortMethodTableId(),
                amLoanMainInfo.getOrganizationId(), amLoanMainInfo.getTenantId());

        loanPlanParamBean.setLoanAmount(loanAmt);//贷款金额
        loanPlanParamBean.setBusinessDate(businessDate);  //当前业务日期
        loanPlanParamBean.setCycle(amLoanMainInfo.getPaymentDateDd());//还款日

        loanPlanParamBean.setTenor(tenor);//期数
        loanPlanParamBean.setInstallmentFeeTable(installmentFee);//费用参数
        loanPlanParamBean.setAmortMethod(amortMethod);//还款方式参数
        loanPlanParamBean.setLoanId(amLoanMainInfo.getLoanId());//订单id
        loanPlanParamBean.setCustomerId(amLoanMainInfo.getCustomerId());//客户id
        loanPlanParamBean.setTenantId(amLoanMainInfo.getTenantId());//租户id
        loanPlanParamBean.setOrganizationId(amLoanMainInfo.getOrganizationId());//机构id
        loanPlanParamBean.setChannel(amLoanMainInfo.getChannel());//渠道
        loanPlanParamBean.setProductId(amLoanMainInfo.getProductId());//产品id
        loanPlanParamBean.setPostingDate(businessDate);//贷款时间
        loanPlanParamBean.setFirstPaymentDay(firstPaymentDay);//首期还款日
        loanPlanParamBean.setWaiveDailyRate(amLoanMainInfo.getActualWaiveDailyRate());
        loanPlanParamBean.setWaiveInterestDays(amLoanMainInfo.getWaiveInterestDays());
        if (StringUtils.isNotBlank(amLoanMainInfo.getPromotionId())) {
            loanPlanParamBean.setLabelCode(amLoanMainInfo.getLabelCode());

            loanPlanParamBean.setPrPromotionInfo(prPromotionInfoService.findByPromotionIdAndTenId(amLoanMainInfo.getPromotionId(), amLoanMainInfo.getTenantId()));
        }

        return loanPlanParamBean;
    }

    @Override
    public LoanPlanParamBean buildTrailLoanPlanParamBean(LoanReq loanReq, LoanParamBean loanParamBean) throws TxnBizException {

        PrInstallmentFeeTable installmentFee =
                installmentFeeTableService.findInstallmentByFeeId(loanParamBean.getFeeTableId(),
                        loanParamBean.getOrg().getOrganizationId(), loanReq.getTenantId());

        PrAmortMethod amortMethod = amortMethodService.findByChargerOptionAndId(loanReq.getAmortMethod(),
                loanParamBean.getOrg().getOrganizationId(), loanReq.getTenantId());

        Date businessDate = loanParamBean.getOrg().getNextProcessingDate();

        LoanPlanParamBean loanPlanParamBean = new LoanPlanParamBean();
        loanPlanParamBean.setBusinessDate(businessDate);  //当前业务日期
        loanPlanParamBean.setLoanAmount(loanReq.getLoanAmount());//贷款金额
        loanPlanParamBean.setCycle(loanParamBean.getPaymentDay());//还款日
        loanPlanParamBean.setMinIntervalDays(TransBizConstant.MIN_INTERVAL_DAYS);//最小间隔天数
        loanPlanParamBean.setTenor(loanReq.getTenor());//期数
        loanPlanParamBean.setInterestTable(loanParamBean.getInterestTable());//利息参数
        loanPlanParamBean.setInstallmentFeeTable(installmentFee);//费用参数
        loanPlanParamBean.setAmortMethod(amortMethod);//还款方式参数
        loanPlanParamBean.setPostingDate(businessDate);//贷款日期
        loanPlanParamBean.setPrProductTable(loanParamBean.getProduct());//产品
        loanPlanParamBean.setPrWaiveInterestTable(loanParamBean.getPrWaiveInterestTable());
        loanPlanParamBean.setFirstPaymentDay(loanParamBean.getFirstPaymentDate());
        PrProductTable prProductTable = loanParamBean.getProduct();
        PrWaiveInterestTable prWaiveInterestTable = loanParamBean.getPrWaiveInterestTable();
        if (prWaiveInterestTable != null) {
            loanPlanParamBean.setWaiveInterestDays(prProductTable.getWaiveInterestDays());
            BigDecimal waiveDayRate = waiveInterestTableService.getWaiveDailyRate(prWaiveInterestTable, businessDate);//日利率
            loanPlanParamBean.setWaiveDailyRate(waiveDayRate);
        }
        loanPlanParamBean.setRightInfo(loanParamBean.getRightInfo());
        if (loanPlanParamBean.getRightInfo() == null) {
            //非客户权益  执行促销活动
            PromotionLabelBean promotionLabelBean = promotionService
                    .matchingPromotion(loanReq.getTenantId(), loanReq.getChannel()
                            , loanParamBean.getCustomer().getFirstLevel().getCustomerId(), prProductTable.getProductId(), businessDate);
            if (promotionLabelBean != null) {
                loanPlanParamBean.setLabelCode(promotionLabelBean.getCmCustomerLabel().getLabelCode());
                loanPlanParamBean.setPrPromotionInfo(promotionLabelBean.getPrPromotionInfo());
            }
        }

        return loanPlanParamBean;
    }

    /**
     * 还款计划生成
     *
     * @param loanPlanParamBean 还款计划参数bean
     * @return 还款计划列表
     * @author zhenghaopu
     * date: 2019/9/24 11:37
     */
    @Override
    public List<AmRepaymentPlanInfo> buildRepaymentList(LoanPlanParamBean loanPlanParamBean) throws TxnBizException {
        List<AmRepaymentPlanInfo> amRepaymentPlanInfoList = null;


        //进位条件
        String carryCond = loanPlanParamBean.getAmortMethod().getCarryCond();
        //根据还款方式进位条件获取 BigDecimal 中的进位计算值
        if (TransBizConstant.CARRY_COND_1.equals(carryCond)) { //四舍五入
            loanPlanParamBean.setCarryCondInt(BigDecimal.ROUND_HALF_UP);
        }
        if (TransBizConstant.CARRY_COND_2.equals(carryCond)) {//直接进位
            loanPlanParamBean.setCarryCondInt(BigDecimal.ROUND_UP);
        }
        if (TransBizConstant.CARRY_COND_3.equals(carryCond)) {//截位
            loanPlanParamBean.setCarryCondInt(BigDecimal.ROUND_DOWN);
        }

        //还款日起息用零利率生成还款计划
        loanPlanParamBean.setDailyRate(BigDecimal.ZERO);//日利率
        loanPlanParamBean.setMonthRate(BigDecimal.ZERO);//月利率
        loanPlanParamBean.setYearRate(BigDecimal.ZERO);//年利率
        //计算日、月、年 利率
        if (!TransBizConstant.START_DATE_FLAG_2.equals(loanPlanParamBean.getInterestTable().getStartDateFlag())) {
            loanPlanParamBean.setDailyRate(interestTableService.getDailyRate(loanPlanParamBean.getInterestTable(), loanPlanParamBean.getBusinessDate()));//日利率
            loanPlanParamBean.setMonthRate(interestTableService.getMonthRate(loanPlanParamBean.getInterestTable(), loanPlanParamBean.getBusinessDate()));//月利率
            loanPlanParamBean.setYearRate(interestTableService.getYearRate(loanPlanParamBean.getInterestTable()));//年利率
        }

        //若不存在首期还款日 则重算首期还款日 （提前还款时有首期还款日）
        if (loanPlanParamBean.getFirstPaymentDay() == null) {
            Date firstPaymentDate = computeFirstPaymentDate(loanPlanParamBean.getBusinessDate(), loanPlanParamBean.getCycle(), loanPlanParamBean.getMinIntervalDays()); //首次还款日
            loanPlanParamBean.setFirstPaymentDay(firstPaymentDate);
        }

        amRepaymentPlanInfoList = computeRepaymentPlan(loanPlanParamBean);

        //通过客户权益 促销活动 重新计算还款计划
        amRepaymentPlanInfoList = this.rebuildRepaymentList(amRepaymentPlanInfoList, loanPlanParamBean);
        return amRepaymentPlanInfoList;
    }

    /**
     * 计算还款计划
     *
     * @param loanPlanParamBean
     * @return
     * @throws TxnBizException
     */
    private List<AmRepaymentPlanInfo> computeRepaymentPlan(LoanPlanParamBean loanPlanParamBean) throws TxnBizException {
        if (logger.isDebugEnabled()) {
            logger.debug("还款计划入参 ：{}", loanPlanParamBean.toString());
        }
        //还款方式 来源还款方式表
        String paymentMethod = loanPlanParamBean.getAmortMethod().getChargeOption();
        List<AmRepaymentPlanInfo> amRepaymentPlanInfoList = null;
        //分期付款（等本等息）
        if (TransBizConstant.PRIN_CHARGE_OPTION_EPEI.equals(paymentMethod)) {
            amRepaymentPlanInfoList = computeEPEI(loanPlanParamBean);
        }
        //等额本息
        if (TransBizConstant.PRIN_CHARGE_OPTION_EPI.equals(paymentMethod)) {
            amRepaymentPlanInfoList = computeEPI(loanPlanParamBean);
        }
        //等额本金
        else if (TransBizConstant.PRIN_CHARGE_OPTION_EP.equals(paymentMethod)) {
            amRepaymentPlanInfoList = computeEP(loanPlanParamBean);
        }
        //按期付息，到期还本付息
        else if (TransBizConstant.PRIN_CHARGE_OPTION_MONTHLY_INTPAY.equals(paymentMethod)) {
            amRepaymentPlanInfoList = computeMonthlyInterestPayEnd(loanPlanParamBean);
        }
        //按月付息，按年还本付息
        else if (TransBizConstant.PRIN_CHARGE_OPTION_MI_YP.equals(paymentMethod)) {
            amRepaymentPlanInfoList = computeMonthlyInterestYearPay(loanPlanParamBean);
        }
        //随见随还 利随本清
        else if (TransBizConstant.PRIN_CHARGE_OPTION_INTPAY.equals(paymentMethod)) {
            amRepaymentPlanInfoList = computeInterestPayEnd(loanPlanParamBean);
        }
        //信用花灵活还款
        else if (TransBizConstant.PRIN_CHARGE_OPTION_CREDITPAY.equals(paymentMethod)) {
            amRepaymentPlanInfoList = computeInterestPayEnd(loanPlanParamBean);
        }
        return amRepaymentPlanInfoList;
    }

    /**
     * 通过客户权益 促销活动 重新计算还款计划
     *
     * @param planList
     * @param loanPlanParamBean
     * @return
     */
    private List<AmRepaymentPlanInfo> rebuildRepaymentList(List<AmRepaymentPlanInfo> planList, LoanPlanParamBean loanPlanParamBean) throws TxnBizException {
        CmCustomerRightInfo rightInfo = loanPlanParamBean.getRightInfo();
        if (rightInfo != null) {
            //使用了客户权益   根据客户权益做后续重算
            String rightType = rightInfo.getRightsType();
            Integer discountDays = rightInfo.getDiscountDays();
            for (AmRepaymentPlanInfo planInfo : planList) {
                //按天免息
                if (CustomerConstant.RIGHTS_TYPE_01.equals(rightType)) {
                    BigDecimal remainPriAmt = planList.stream().filter(plan -> plan.getBillingTenor() >= planInfo.getBillingTenor())
                            .map(AmRepaymentPlanInfo::getPrincipalAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                    discountDays = this.discountByDay(planInfo, remainPriAmt, discountDays);
                }

                if (CustomerConstant.RIGHTS_TYPE_02.equals(rightType)) {
                    this.discountByRate(planInfo, rightInfo);
                }
            }
        }
        //没有客户权益   没有使用优惠券   促销活动
        else if (loanPlanParamBean.getPrPromotionInfo() != null) {

            PrPromotionInfo prPromotionInfo = loanPlanParamBean.getPrPromotionInfo();

            //1 = 利率打折 2 = 固定利率
            String rateFlag = prPromotionInfo.getRateFlag();
            //1 = 利率打折
            if (TransBizConstant.PROMOTION_RATE_FLAG_1.equals(rateFlag)) {
                planList.stream().forEach(amRepaymentPlanInfo -> discountByRate(amRepaymentPlanInfo, prPromotionInfo.getRateDiscountPercentage()));
            }
            //2 = 固定利率
            else if (TransBizConstant.PROMOTION_RATE_FLAG_2.equals(rateFlag)) {
                loanPlanParamBean.setOriginalDailyRate(loanPlanParamBean.getDailyRate());

                PrInterestTable interestTable = interestTableService.findInterestByInterestId(prPromotionInfo.getInterestTableId(), prPromotionInfo.getTenantId());

                loanPlanParamBean.setInterestTable(interestTable);
                //计算日、月、年 利率
                loanPlanParamBean.setDailyRate(interestTableService.getDailyRate(interestTable, loanPlanParamBean.getBusinessDate()));//日利率
                loanPlanParamBean.setMonthRate(interestTableService.getMonthRate(interestTable, loanPlanParamBean.getBusinessDate()));//月利率
                loanPlanParamBean.setYearRate(interestTableService.getYearRate(interestTable));//年利率

                //根据促销活动的利率  计算新的还款计划
                List<AmRepaymentPlanInfo> newPlanList = computeRepaymentPlan(loanPlanParamBean);
                Map<Integer, AmRepaymentPlanInfo> newPlanMap = newPlanList.stream().collect(Collectors.toMap(AmRepaymentPlanInfo::getBillingTenor, Function.identity()));

                for (AmRepaymentPlanInfo planInfo : planList) {
                    AmRepaymentPlanInfo newPlanInfo = newPlanMap.get(planInfo.getBillingTenor());

                    //根据原还款计划计算优惠金额
                    BigDecimal discountIntr = planInfo.getInterestAmount().subtract(newPlanInfo.getInterestAmount());

                    if (discountIntr.compareTo(BigDecimal.ZERO) < 0) {
                        discountIntr = BigDecimal.ZERO;
                    }
                    newPlanInfo.setWaivedAmount(discountIntr);
                }
                planList = newPlanList;
            }
        }
        return planList;
    }

    /**
     * 按天免息,重算还款计划
     *
     * @param planInfo
     */
    private int discountByDay(AmRepaymentPlanInfo planInfo, BigDecimal remainPriAmt, Integer discountDays) {
        if (discountDays <= 0) {
            return 0;
        }

        //当期起息日
        Date intrStartDateTenor = DateUtils.addDay(planInfo.getIntrStartDate(), discountDays);
        //当期折扣天数
        int discountTenor = discountDays;
        //如果折扣天数大于当期天数
        if (intrStartDateTenor.compareTo(planInfo.getPaymentDueDate()) > 0) {
            //起息日为还款日当天
            intrStartDateTenor = planInfo.getPaymentDueDate();
            //重算当期折扣天数
            discountTenor = DateUtils.getDaysBetween(planInfo.getIntrStartDate(), planInfo.getPaymentDueDate());
        }

        //当期计息天数
        int intrDaysTenor = DateUtils.getDaysBetween(intrStartDateTenor, planInfo.getPaymentDueDate());
        //剩余折扣天数
        planInfo.setIntrStartDate(intrStartDateTenor);
        //剩余本金*当期计息天数天数*日利率
        BigDecimal afterDiscountIntr = remainPriAmt.multiply(BigDecimal.valueOf(intrDaysTenor)).multiply(planInfo.getActualDailyRate()).setScale(2, BigDecimal.ROUND_HALF_UP);
        BigDecimal discountIntr = planInfo.getInterestAmount().subtract(afterDiscountIntr);
        planInfo.setInterestAmount(afterDiscountIntr);
        if (discountIntr.compareTo(BigDecimal.ZERO) < 0) {
            discountIntr = BigDecimal.ZERO;
        }
        planInfo.setWaivedAmount(discountIntr);
        planInfo.setTotalAmount(planInfo.getPrincipalAmount().add(planInfo.getInterestAmount()).add(planInfo.getFeeAmount()));
        discountDays = discountDays - discountTenor;
        return discountDays;
    }

    /**
     * 按客户权益 利率打折
     *
     * @param planInfo
     * @param rightInfo
     * @return
     */
    private void discountByRate(AmRepaymentPlanInfo planInfo, CmCustomerRightInfo rightInfo) {
        String discountType = rightInfo.getDiscountType();
        if (!CustomerConstant.DISCOUNT_TYPE_1.equals(discountType)) {
            return;
        }
        int discountTerms = rightInfo.getDiscountTerms();
        if (planInfo.getBillingTenor() > discountTerms) {
            return;
        }
        discountByRate(planInfo, rightInfo.getDiscountRate());
    }

    /**
     * 按利率折扣打折
     *
     * @param planInfo
     * @param discounteRate
     */
    private void discountByRate(AmRepaymentPlanInfo planInfo, BigDecimal discounteRate) {
        BigDecimal afterDiscountRate = planInfo.getActualDailyRate().multiply(discounteRate).setScale(CommonContants.BIGDECIMAL_SCALE_7, BigDecimal.ROUND_HALF_UP);
        BigDecimal afterDiscountIntr = planInfo.getInterestAmount().multiply(discounteRate).setScale(CommonContants.BIGDECIMAL_SCALE, BigDecimal.ROUND_HALF_UP);
        BigDecimal discountIntr = planInfo.getInterestAmount().subtract(afterDiscountIntr);
        planInfo.setActualDailyRate(afterDiscountRate);
        planInfo.setInterestAmount(afterDiscountIntr);
        if (discountIntr.compareTo(BigDecimal.ZERO) < 0) {
            discountIntr = BigDecimal.ZERO;
        }
        planInfo.setWaivedAmount(discountIntr);
        planInfo.setTotalAmount(planInfo.getPrincipalAmount().add(planInfo.getInterestAmount()).add(planInfo.getFeeAmount()));
    }


    /**
     * 等额本息计算
     *
     * @param loanPlanParamBean 还款计划参数
     * @return java.util.List<com.jrx.anytxn.transaction.entity.AmRepaymentPlanInfo>
     * @author : zhenghaopu
     * date: 2019/9/25
     */
    private List<AmRepaymentPlanInfo> computeEPI(LoanPlanParamBean loanPlanParamBean) throws TxnBizException {
        BigDecimal loanAmount = loanPlanParamBean.getLoanAmount();//贷款金额
        BigDecimal monthRate = loanPlanParamBean.getMonthRate();//月利率
        Integer tenor = loanPlanParamBean.getTenor();//总期数
        Integer dcimPlace = loanPlanParamBean.getAmortMethod().getDcimPlace();//小数位
        Integer carryCondInt = loanPlanParamBean.getCarryCondInt();//进位条件
        String calcMethod = loanPlanParamBean.getInstallmentFeeTable().getChargeBasis();//费用计算基础

        BigDecimal remainAmount = loanAmount;   //剩余本金
        BigDecimal monthlyPaymentAmount;//月还款金额
        BigDecimal monthlyFee;//月还费用

        // 判断月利率是否是0
        if (BigDecimal.ZERO.compareTo(monthRate) >= 0) {
            // 如果这里的月利率为0，则不代入公式进行计算，月还款金额 = 本金 / 期数
            monthlyPaymentAmount =
                    loanAmount.divide(BigDecimal.valueOf(tenor), TransBizConstant.BIGDECIMAL_SCALE_10, carryCondInt).setScale(dcimPlace, carryCondInt);
        } else {
            //定义平方计算依据
            MathContext mc = new MathContext(TransBizConstant.ZERO, RoundingMode.valueOf(carryCondInt));
            // 公式：月还款金额 = (本金 * 月利率 * ((月利率 + 1)↗期数)) / (((月利率 + 1)↗期数) - 1)
            monthlyPaymentAmount = loanAmount.multiply(monthRate).multiply(monthRate.add(BigDecimal.ONE).pow(tenor, mc))
                    .divide(monthRate.add(BigDecimal.ONE).pow(tenor, mc).subtract(BigDecimal.ONE),
                            TransBizConstant.BIGDECIMAL_SCALE_10, carryCondInt).setScale(dcimPlace, carryCondInt);
        }
        //首期还款日
        Date firstPaymentDate = loanPlanParamBean.getFirstPaymentDay();

        Calendar cal = Calendar.getInstance();
        cal.setTime(loanPlanParamBean.getBusinessDate());
        int day = cal.get(Calendar.DATE);

        //贴息天数
        Integer waiveInterestDays = loanPlanParamBean.getWaiveInterestDays();

        List<AmRepaymentPlanInfo> list = new ArrayList<>();
        //循环期数 生成还款计划
        for (int i = 1; i <= tenor; i++) {
            AmRepaymentPlanInfo repaymentPlanInfo = buildRepaymentPlanInfo(loanPlanParamBean);
            BigDecimal interest = BigDecimal.ZERO;
            BigDecimal princial;
            int intrDays = 0;
            if (i == 1) {
                if (waiveInterestDays <= 0) {
                    BigDecimal monInterest = remainAmount.multiply(monthRate).setScale(dcimPlace, carryCondInt);
                    //按照首期计算方式获取月利息
                    interest = getMonthInterest(loanPlanParamBean, monInterest, remainAmount, i, firstPaymentDate, loanPlanParamBean.getPostingDate(), firstPaymentDate, loanPlanParamBean.getDailyRate(), dcimPlace, carryCondInt);
                } else {
                    //贴息
                    int days = DateUtils.getDaysBetween(loanPlanParamBean.getBusinessDate(), firstPaymentDate);
                    intrDays = Math.max(days - waiveInterestDays, 0);
                    interest = remainAmount.multiply(loanPlanParamBean.getDailyRate()).multiply(BigDecimal.valueOf(intrDays)).setScale(dcimPlace, carryCondInt);//利息
                }
                //如果只贷1期，那么本金就是贷款金额，不能通过计算得到，可能由于计算误差，差1分钱 add by jing.xiaoyu 20170831
                if (tenor == 1) {
                    princial = loanAmount;
                } else {
                    princial = monthlyPaymentAmount.subtract(interest).setScale(dcimPlace, carryCondInt);//本金
                }
            } else {
                Date thisPaymentDueDate = getCurrentPaymentDueDate(firstPaymentDate, i);
                Date lastPaymentDueDate = getPaymentDueDate(list, i - 1);
                if (waiveInterestDays > 0) {
                    //贴息
                    //还款计划改造 判断是否日对日，如果不是日对日 取下月最后一天
                    int days = DateUtils.getDaysBetween(thisPaymentDueDate, lastPaymentDueDate);
                    intrDays = Math.max(days - waiveInterestDays, 0);
                    interest = remainAmount.multiply(loanPlanParamBean.getDailyRate()).multiply(BigDecimal.valueOf(intrDays)).setScale(dcimPlace, carryCondInt);//利息
                } else {
                    BigDecimal monInterest = remainAmount.multiply(monthRate).setScale(dcimPlace, carryCondInt);
                    //按照首期计算方式获取月利息
                    interest = getMonthInterest(loanPlanParamBean, monInterest, remainAmount, i, firstPaymentDate, lastPaymentDueDate, thisPaymentDueDate, loanPlanParamBean.getDailyRate(), dcimPlace, carryCondInt);
                }

                if (i == tenor) {
                    princial = remainAmount.setScale(dcimPlace, carryCondInt);//本金
                } else {
                    princial = monthlyPaymentAmount.subtract(interest).setScale(dcimPlace, carryCondInt);//本金
                }
            }

            //计算每月费用
            BigDecimal calcAmnt = BigDecimal.ZERO;
            if (TransBizConstant.FEE_CALC_METHOD_1.equals(calcMethod)) {
                calcAmnt = loanAmount;
            } else if (TransBizConstant.FEE_CALC_METHOD_2.equals(calcMethod)) {
                calcAmnt = princial;
            } else if (TransBizConstant.FEE_CALC_METHOD_0.equals(calcMethod)) {
                calcAmnt = remainAmount;
            }
            monthlyFee = getFeeAmnt(i, calcAmnt, loanPlanParamBean);//月还款费用
            remainAmount = remainAmount.subtract(princial);//剩余本金刷新 减去当期本金

            //loanAmount = loanAmount.subtract(princial);//剩余金额
            repaymentPlanInfo.setPrincipalAmount(princial);//本金
            repaymentPlanInfo.setInterestAmount(interest);//利息
            repaymentPlanInfo.setBillingTenor(i);//期数
            repaymentPlanInfo.setTotalAmount(princial.add(interest).add(monthlyFee).setScale(dcimPlace, carryCondInt));//当期还款总金额
            repaymentPlanInfo.setFeeAmount(monthlyFee);//费用
            Date intrStartDate;
            if (i == 1) {
                repaymentPlanInfo.setPaymentDueDate(firstPaymentDate);
                intrStartDate = DateUtils.getDate(loanPlanParamBean.getPostingDate(), loanPlanParamBean.getWaiveInterestDays());
            } else {
                repaymentPlanInfo.setPaymentDueDate(getCurrentPaymentDueDate(firstPaymentDate, i));//还款日期
                intrStartDate = DateUtils.getDate(getPaymentDueDate(list, i - 1), loanPlanParamBean.getWaiveInterestDays());
            }

            if (waiveInterestDays > 0 && intrDays == 0) {
                intrStartDate = repaymentPlanInfo.getPaymentDueDate();
            }
            repaymentPlanInfo.setIntrStartDate(intrStartDate);
            list.add(repaymentPlanInfo);
        }
        return list;
    }

    /**
     * 等额本金
     *
     * @param loanPlanParamBean 还款计划参数
     * @return java.util.List<com.jrx.anytxn.transaction.entity.AmRepaymentPlanInfo>
     * @author : zhenghaopu
     * date: 2019/9/25
     */
    private List<AmRepaymentPlanInfo> computeEP(LoanPlanParamBean loanPlanParamBean) throws TxnBizException {

        BigDecimal loanAmount = loanPlanParamBean.getLoanAmount(); //贷款金额
        Integer tenor = loanPlanParamBean.getTenor(); //总期数
        int cycle = loanPlanParamBean.getCycle();//还款日
        Integer dcimPlace = loanPlanParamBean.getAmortMethod().getDcimPlace();//小数位
        Integer carryCondInt = loanPlanParamBean.getCarryCondInt();//进位条件
        String calcMethod = loanPlanParamBean.getInstallmentFeeTable().getChargeBasis();//费用计算基础
        Integer waiveInterestDays = loanPlanParamBean.getWaiveInterestDays();//贴息天数

        BigDecimal remainAmount = loanAmount; //剩余本金
        BigDecimal monthlyFee;//每月费用
        int intrDays = 0;
        //每月本金=贷款金额 / 期数
        BigDecimal monthlyPrincial = loanAmount.divide(BigDecimal.valueOf(tenor), dcimPlace, carryCondInt);
        // 首期还款日
        Date firstPaymentDate = loanPlanParamBean.getFirstPaymentDay(); //首次还款日

        List<AmRepaymentPlanInfo> list = new ArrayList<>();
        //循环期数 生成还款计划
        for (int i = 1; i <= tenor; i++) {
            AmRepaymentPlanInfo repaymentPlanInfo = buildRepaymentPlanInfo(loanPlanParamBean);

            //最后一期（剩余贷款金额）
            if (i == tenor) {
                monthlyPrincial = remainAmount.setScale(dcimPlace, carryCondInt);
            }

            //计算每月费用
            BigDecimal calcAmnt = BigDecimal.ZERO;
            if (TransBizConstant.FEE_CALC_METHOD_1.equals(calcMethod)) {//按贷款本金
                calcAmnt = loanAmount;
            } else if (TransBizConstant.FEE_CALC_METHOD_2.equals(calcMethod)) {//按每期本金
                calcAmnt = monthlyPrincial;
            } else if (TransBizConstant.FEE_CALC_METHOD_0.equals(calcMethod)) {//按剩余本金
                calcAmnt = remainAmount;
            }
            //每月费用
            monthlyFee = getFeeAmnt(tenor, calcAmnt, loanPlanParamBean);
            //按月利率计算本期应收利息
            BigDecimal interest = remainAmount.multiply(loanPlanParamBean.getMonthRate()).setScale(dcimPlace, carryCondInt);//利息

            Date thisPaymentDueDate = getCurrentPaymentDueDate(firstPaymentDate, i);//当期还款日
            Date lastPaymentDueDate = loanPlanParamBean.getPostingDate();
            //非首期重新计算还款日
            if (i != 1) {
                lastPaymentDueDate = this.getPaymentDueDate(list, i - 1);
            }

            if (waiveInterestDays > 0) {
                //贴息
                int days = DateUtils.getDaysBetween(thisPaymentDueDate, lastPaymentDueDate);
                intrDays = Math.max(days - waiveInterestDays, 0);
                interest = remainAmount.multiply(loanPlanParamBean.getDailyRate()).multiply(BigDecimal.valueOf(intrDays)).setScale(dcimPlace, carryCondInt);//利息
            } else {
                //按照首期 还款计划 计算方式计算利息
                interest = getMonthInterest(loanPlanParamBean, interest, remainAmount, i, firstPaymentDate, lastPaymentDueDate, thisPaymentDueDate, loanPlanParamBean.getDailyRate(), dcimPlace, carryCondInt);
            }

            remainAmount = remainAmount.subtract(monthlyPrincial);//剩余本金刷新 减掉当期本金
            repaymentPlanInfo.setPrincipalAmount(monthlyPrincial);//本金
            repaymentPlanInfo.setTotalAmount(monthlyPrincial.add(interest).add(monthlyFee).setScale(dcimPlace, carryCondInt));//还款总金额
            repaymentPlanInfo.setInterestAmount(interest);//利息
            repaymentPlanInfo.setFeeAmount(monthlyFee);//费用
            repaymentPlanInfo.setBillingTenor(i);//期数
            Date intrStartDate;
            if (i == 1) {
                repaymentPlanInfo.setPaymentDueDate(firstPaymentDate);
                intrStartDate = DateUtils.getDate(loanPlanParamBean.getPostingDate(), loanPlanParamBean.getWaiveInterestDays());
            } else {
                repaymentPlanInfo.setPaymentDueDate(getCurrentPaymentDueDate(firstPaymentDate, i));//还款日期
                intrStartDate = DateUtils.getDate(getPaymentDueDate(list, i - 1), loanPlanParamBean.getWaiveInterestDays());
            }

            if (waiveInterestDays > 0 && intrDays == 0) {
                intrStartDate = repaymentPlanInfo.getPaymentDueDate();
            }
            repaymentPlanInfo.setIntrStartDate(intrStartDate);
            list.add(repaymentPlanInfo);
        }
        return list;
    }

    /**
     * 按月付息，按年还本
     *
     * @param loanPlanParamBean 还款计划参数
     * @return java.util.List<com.jrx.anytxn.transaction.entity.AmRepaymentPlanInfo>
     * @author : zhenghaopu
     * date: 2019/9/25
     */
    private List<AmRepaymentPlanInfo> computeMonthlyInterestYearPay(LoanPlanParamBean loanPlanParamBean) throws TxnBizException {
        BigDecimal loanAmount = loanPlanParamBean.getLoanAmount();//贷款金额
        Integer tenor = loanPlanParamBean.getTenor();  //剩余期数
        Integer dcimPlace = loanPlanParamBean.getAmortMethod().getDcimPlace();//小数位
        Integer carryCondInt = loanPlanParamBean.getCarryCondInt();//进位条件
        BigDecimal monthlyRate = loanPlanParamBean.getMonthRate();//月利率
        Integer castTenor = loanPlanParamBean.getCastTenor(); //已抛期数
        Integer waiveInterestDays = loanPlanParamBean.getWaiveInterestDays();
        int calTenor = 0; //参与计算的期数
        if (castTenor != null) {
            calTenor = castTenor - 1;
        }

        BigDecimal remainAmount = loanAmount; //剩余本金
        BigDecimal monthlyFee;//月费用
        int intrDays = 0;
        //月还款本金
        BigDecimal yearPrincial = loanAmount.divide(new BigDecimal(tenor % TransBizConstant.MONTH == 0 ? tenor / TransBizConstant.MONTH :
                tenor / TransBizConstant.MONTH + 1), dcimPlace, carryCondInt);
        //月利息
        BigDecimal monthlyInterest = loanAmount.multiply(monthlyRate).setScale(dcimPlace, carryCondInt);
        BigDecimal intrAmt = BigDecimal.ZERO;

        //首期还款日
        Date firstPaymentDate = loanPlanParamBean.getFirstPaymentDay(); //首次还款日

        //获取业务日期在本月中是第几日
        Calendar cal = Calendar.getInstance();
        cal.setTime(loanPlanParamBean.getBusinessDate());
        int day = cal.get(Calendar.DATE);

        //还款计划
        List<AmRepaymentPlanInfo> list = new ArrayList<>();
        //循环期数 生成还款计划
        for (int i = 1; i <= tenor; i++) {
            AmRepaymentPlanInfo repaymentPlanInfo = buildRepaymentPlanInfo(loanPlanParamBean);
            //计算本金和利息
            if (i == 1) {//首期（由于首期中天数存在跨月的情况，分离开来）
                BigDecimal firstMonthlyInterest = BigDecimal.ZERO;
                if (waiveInterestDays > 0) {
                    int days = DateUtils.getDaysBetween(loanPlanParamBean.getBusinessDate(), firstPaymentDate);
                    intrDays = Math.max(days - waiveInterestDays, 0);
                    firstMonthlyInterest = loanAmount.multiply(loanPlanParamBean.getDailyRate()).multiply(BigDecimal.valueOf(intrDays)).setScale(dcimPlace, carryCondInt);
                } else {
                    //还款计划改造
                    firstMonthlyInterest = getMonthInterest(loanPlanParamBean, monthlyInterest, loanAmount, i, firstPaymentDate, loanPlanParamBean.getBusinessDate(), firstPaymentDate, loanPlanParamBean.getDailyRate(), dcimPlace, carryCondInt);
                }

                if (i == tenor) {//只贷一期
                    logger.debug("第一期处理逻辑，最后一期，还款加入本金金额:{}", remainAmount);
                    repaymentPlanInfo.setPrincipalAmount(remainAmount);//本金
                } else if ((i + calTenor) % TransBizConstant.MONTH == 0) {//如果本期是第一期，同样是最后一期或者除以12余数为零，需要设置剩余本金
                    logger.debug("第一期处理逻辑，12期整数倍，不是最后一期，还款加入本金金额:{}", yearPrincial);
                    repaymentPlanInfo.setPrincipalAmount(yearPrincial);//本金
                } else {//其他情况
                    logger.debug("第一期处理逻辑，还款不加入本金");
                    repaymentPlanInfo.setPrincipalAmount(BigDecimal.ZERO);//本金
                }
                repaymentPlanInfo.setInterestAmount(firstMonthlyInterest);//利息
            } else {
                //计算贴息天数
                Date thisPaymentDueDate = getCurrentPaymentDueDate(firstPaymentDate, i);
                Date lastPaymentDueDate = this.getPaymentDueDate(list, i - 1);

                int days = DateUtils.getDaysBetween(thisPaymentDueDate, lastPaymentDueDate);
                //本次计划执行完成之后，进行剩余本金计算，剩余期数利息计算
                intrDays = Math.max(days - waiveInterestDays, 0);
                if (waiveInterestDays > 0) {
                    //贴息
                    intrAmt = remainAmount.multiply(loanPlanParamBean.getDailyRate()).multiply(BigDecimal.valueOf(intrDays)).setScale(dcimPlace, carryCondInt);
                } else {
                    intrAmt = getMonthInterest(loanPlanParamBean, monthlyInterest, remainAmount, i, firstPaymentDate, lastPaymentDueDate, thisPaymentDueDate, loanPlanParamBean.getDailyRate(), dcimPlace, carryCondInt);
                }

                if ((i + calTenor) % TransBizConstant.MONTH == 0 && i != tenor) {//十二个月的整数倍（此时需要还本金，分离开来）
                    //repaymentPlanInfo.setTotalAmount(yearPrincial.add(monthlyInterest).add(monthlyFee).setScale
                    // (dcimPlace, carryCondInt));//总金额
                    repaymentPlanInfo.setPrincipalAmount(yearPrincial);
                    repaymentPlanInfo.setInterestAmount(intrAmt);//利息
                } else if (i == tenor) {//尾期（此时需要还本金，分离开来）
                    //repaymentPlanInfo.setTotalAmount(remainAmount.add(monthlyInterest).add(monthlyFee).setScale
                    // (dcimPlace, carryCondInt));//总金额
                    repaymentPlanInfo.setPrincipalAmount(remainAmount);
                    repaymentPlanInfo.setInterestAmount(intrAmt);//利息
                } else {
                    repaymentPlanInfo.setPrincipalAmount(BigDecimal.ZERO);//本金
                    repaymentPlanInfo.setInterestAmount(intrAmt);//利息
                }
            }

            //计算每月费用
            BigDecimal calcAmnt = BigDecimal.ZERO;
            //按贷款本金计算
            if (TransBizConstant.FEE_CALC_METHOD_1.equals(loanPlanParamBean.getInstallmentFeeTable().getChargeBasis())) {
                calcAmnt = loanAmount;

            } else if (TransBizConstant.FEE_CALC_METHOD_2.equals(loanPlanParamBean.getInstallmentFeeTable().getChargeBasis())) {//按每期本金计算
                calcAmnt = repaymentPlanInfo.getPrincipalAmount();
            } else if (TransBizConstant.FEE_CALC_METHOD_0.equals(loanPlanParamBean.getInstallmentFeeTable().getChargeBasis())) {//按剩余本金计算
                calcAmnt = remainAmount;
            }
            monthlyFee = getFeeAmnt(i, calcAmnt, loanPlanParamBean);
            repaymentPlanInfo.setFeeAmount(monthlyFee);//费用
            repaymentPlanInfo.setTotalAmount(repaymentPlanInfo.getFeeAmount().add(repaymentPlanInfo.getInterestAmount().add(repaymentPlanInfo.getPrincipalAmount())).setScale(dcimPlace, carryCondInt));
            repaymentPlanInfo.setBillingTenor(i);//期数


            if ((i + calTenor) % TransBizConstant.MONTH == 0 || i == tenor) {
                remainAmount = remainAmount.subtract(yearPrincial);
                monthlyInterest = remainAmount.multiply(monthlyRate).setScale(dcimPlace, carryCondInt);

            }

            Date intrStartDate;
            if (i == 1) {
                repaymentPlanInfo.setPaymentDueDate(firstPaymentDate);
                intrStartDate = DateUtils.getDate(loanPlanParamBean.getPostingDate(), loanPlanParamBean.getWaiveInterestDays());
            } else {
                repaymentPlanInfo.setPaymentDueDate(getCurrentPaymentDueDate(firstPaymentDate, i));//还款日期
                intrStartDate = DateUtils.getDate(getPaymentDueDate(list, i - 1), loanPlanParamBean.getWaiveInterestDays());
            }
            if (waiveInterestDays > 0 && intrDays == 0) {
                intrStartDate = repaymentPlanInfo.getPaymentDueDate();
            }
            repaymentPlanInfo.setIntrStartDate(intrStartDate);
            list.add(repaymentPlanInfo);
        }
        return list;
    }

    /**
     * 分期付款（等本等息）
     *
     * @param loanPlanParamBean 还款计划参数
     * @return java.util.List<com.jrx.anytxn.transaction.entity.AmRepaymentPlanInfo>
     * @author : zhenghaopu
     * date: 2019/9/25
     */
    private List<AmRepaymentPlanInfo> computeEPEI(LoanPlanParamBean loanPlanParamBean) throws TxnBizException {
        Integer tenor = loanPlanParamBean.getTenor(); //总期数
        Integer dcimPlace = loanPlanParamBean.getAmortMethod().getDcimPlace();//小数位
        Integer carryCondInt = loanPlanParamBean.getCarryCondInt(); //进位条件
        Integer waiveInterestDays = loanPlanParamBean.getWaiveInterestDays();//贴息天数
        Date businessDate = loanPlanParamBean.getBusinessDate();//业务日期
        PrInterestTable prInterestTable = loanPlanParamBean.getInterestTable();

        BigDecimal remainAmount = loanPlanParamBean.getLoanAmount();   //剩余本金

        //月利率 公式: 年利率 * (1 + 期数) / (24 * 期数)   为准确计算  将除(24 * 期数) 放到了月利率计算公式中
        BigDecimal monthRate =
                loanPlanParamBean.getYearRate().multiply(BigDecimal.valueOf(1L + tenor)).divide(BigDecimal.valueOf(24L * tenor), TransBizConstant.BIGDECIMAL_SCALE_7,
                        BigDecimal.ROUND_HALF_UP);
        int monthBase = interestTableService.getMonthBase(prInterestTable.getMonthBase(), businessDate);
        //日利率
        BigDecimal dailyRate = monthRate.divide(
                BigDecimal.valueOf(monthBase), TransBizConstant.BIGDECIMAL_SCALE_7, BigDecimal.ROUND_HALF_UP);
        loanPlanParamBean.setDailyRate(dailyRate);
        //月利息 公式: 借款本金 * 月利率
        BigDecimal monthIntr =
                loanPlanParamBean.getLoanAmount().multiply(monthRate).setScale(dcimPlace, carryCondInt);
        //每期本金= 贷款金额/总期数
        BigDecimal prePrincial = loanPlanParamBean.getLoanAmount().divide(BigDecimal.valueOf(tenor), dcimPlace, carryCondInt);

        //计算首期还款日
        Date firstPaymentDate = loanPlanParamBean.getFirstPaymentDay();//首次还款日

        List<AmRepaymentPlanInfo> list = new ArrayList<>();
        for (int i = 1; i <= tenor; i++) {
            AmRepaymentPlanInfo repaymentPlanInfo = buildRepaymentPlanInfo(loanPlanParamBean);
            repaymentPlanInfo.setActualDailyRate(dailyRate);
            BigDecimal princial = prePrincial;
            //本金扎差
            if (i == tenor) { //最后一期本金按剩余本经计算
                princial = remainAmount.setScale(dcimPlace, carryCondInt);
            }

            //计算每月费用
            BigDecimal calcAmnt = BigDecimal.ZERO;
            //按贷款本金计算
            if (TransBizConstant.FEE_CALC_METHOD_1.equals(loanPlanParamBean.getInstallmentFeeTable().getChargeBasis())) {
                calcAmnt = loanPlanParamBean.getLoanAmount();
            } else if (TransBizConstant.FEE_CALC_METHOD_2.equals(loanPlanParamBean.getInstallmentFeeTable().getChargeBasis())) {//按每期本金计算
                calcAmnt = princial;
            } else if (TransBizConstant.FEE_CALC_METHOD_0.equals(loanPlanParamBean.getInstallmentFeeTable().getChargeBasis())) {//按剩余本金计算
                calcAmnt = remainAmount;
            }
            BigDecimal fee = getFeeAmnt(i, calcAmnt, loanPlanParamBean);
            repaymentPlanInfo.setFeeAmount(fee);//费用


            repaymentPlanInfo.setPrincipalAmount(princial);//本金

            //贴息
            int intrDays = 0;
            if (waiveInterestDays > 0) {
                Date thisPaymentDueDate;
                Date lastPaymentDueDate;
                if (i == 1) {
                    thisPaymentDueDate = firstPaymentDate;
                    lastPaymentDueDate = loanPlanParamBean.getPostingDate();
                } else {
                    //还款计划改造 判断是否日对日，如果不是日对日 取下月最后一天
                    thisPaymentDueDate = getCurrentPaymentDueDate(firstPaymentDate, i);
                    lastPaymentDueDate = getPaymentDueDate(list, i - 1);
                }
                int days = DateUtils.getDaysBetween(thisPaymentDueDate, lastPaymentDueDate);
                intrDays = Math.max(days - waiveInterestDays, 0);
                monthIntr = loanPlanParamBean.getLoanAmount().multiply(dailyRate).multiply(BigDecimal.valueOf(intrDays)).setScale(dcimPlace, carryCondInt);
            }

            repaymentPlanInfo.setInterestAmount(monthIntr); //当期利息
            repaymentPlanInfo.setBillingTenor(i);//期数
            repaymentPlanInfo.setTotalAmount(princial.add(monthIntr).add(fee).setScale(dcimPlace, carryCondInt));//还款总金额

            Date intrStartDate;
            if (i == 1) {
                //首期 按照首期计算方式计算利息 如果有贴息按照原逻辑
                if (waiveInterestDays == 0) {
                    //按照首期计算方式获取月利息
                    BigDecimal newMonthIntr = getMonthInterest(loanPlanParamBean, monthIntr, loanPlanParamBean.getLoanAmount(), i, firstPaymentDate, loanPlanParamBean.getPostingDate(), firstPaymentDate, dailyRate, dcimPlace, carryCondInt);
                    repaymentPlanInfo.setInterestAmount(newMonthIntr); //当期利息
                    //还款总金额
                    repaymentPlanInfo.setTotalAmount(princial.add(newMonthIntr).add(fee).setScale(dcimPlace, carryCondInt));
                }
                repaymentPlanInfo.setPaymentDueDate(firstPaymentDate);
                intrStartDate = DateUtils.getDate(loanPlanParamBean.getPostingDate(), loanPlanParamBean.getWaiveInterestDays());
            } else {
                //还款计划改造 判断是否日对日，如果不是日对日 取下月最后一天
                Date currPaymenrDueDate = getCurrentPaymentDueDate(firstPaymentDate, i);
                //获取上一期的还款日
                Date lastPaymentDueDate = getPaymentDueDate(list, i - 1);
                //非首期 按照还款计划计算方式计算利息 如果有贴息按照原逻辑
                if (waiveInterestDays == 0) {
                    //按照首期计算方式获取月利息
                    BigDecimal newMonthIntr = getMonthInterest(loanPlanParamBean, monthIntr, loanPlanParamBean.getLoanAmount(), i, firstPaymentDate, lastPaymentDueDate, currPaymenrDueDate, dailyRate, dcimPlace, carryCondInt);
                    //当期利息
                    repaymentPlanInfo.setInterestAmount(newMonthIntr);
                    //还款总金额
                    repaymentPlanInfo.setTotalAmount(princial.add(newMonthIntr).add(fee).setScale(dcimPlace, carryCondInt));
                }
                //还款计划改造 判断是否日对日，如果不是日对日 取下月最后一天
                repaymentPlanInfo.setPaymentDueDate(currPaymenrDueDate);
                intrStartDate = DateUtils.getDate(lastPaymentDueDate, loanPlanParamBean.getWaiveInterestDays());
            }

            if (waiveInterestDays > 0 && intrDays == 0) {
                intrStartDate = repaymentPlanInfo.getPaymentDueDate();
            }

            //起息日
            repaymentPlanInfo.setIntrStartDate(intrStartDate);

            remainAmount = remainAmount.subtract(princial);//剩余金额重新计算
            list.add(repaymentPlanInfo);
        }
        return list;
    }

    private Date getPaymentDueDate(List<AmRepaymentPlanInfo> planInfoList, int tenor) {
        return planInfoList.stream().filter(plan -> plan.getBillingTenor() == tenor).findFirst().get().getPaymentDueDate();
    }


    /**
     * 按月付息，到期还本付息
     *
     * @param loanPlanParamBean 还款计划参数
     * @return java.util.List<com.jrx.anytxn.transaction.entity.AmRepaymentPlanInfo>
     * @author : zhenghaopu
     * date: 2019/9/25
     */
    private List<AmRepaymentPlanInfo> computeMonthlyInterestPayEnd(LoanPlanParamBean loanPlanParamBean) throws TxnBizException {
        BigDecimal loanAmount = loanPlanParamBean.getLoanAmount();//贷款总金额
        Integer tenor = loanPlanParamBean.getTenor(); //总期数
        Integer dcimPlace = loanPlanParamBean.getAmortMethod().getDcimPlace(); //小数位
        Integer carryCondInt = loanPlanParamBean.getCarryCondInt();//进位条件
        String calcMethod = loanPlanParamBean.getInstallmentFeeTable().getChargeBasis();//获取费用计算基础
        Integer waiveInterestDays = loanPlanParamBean.getWaiveInterestDays();

        BigDecimal monthlyFee;//月费用
        int intrDays = 0;

        //月利息
        BigDecimal monthlyInterest = loanAmount.multiply(loanPlanParamBean.getMonthRate()).setScale(dcimPlace, carryCondInt);
        //首期还款日
        Date firstPaymentDate = loanPlanParamBean.getFirstPaymentDay(); //首次还款日

        //当前业务日期是每个月的第几天
        Calendar cal = Calendar.getInstance();
        cal.setTime(loanPlanParamBean.getBusinessDate());
        int day = cal.get(Calendar.DATE);

        //还款计划列表
        List<AmRepaymentPlanInfo> list = new ArrayList<>();
        //循环期数 生成还款计划
        for (int i = 1; i <= tenor; i++) {
            AmRepaymentPlanInfo repaymentPlanInfo = buildRepaymentPlanInfo(loanPlanParamBean);
            //计算每月费用
            BigDecimal calcAmnt = BigDecimal.ZERO;
            if (TransBizConstant.FEE_CALC_METHOD_1.equals(calcMethod)) {//费用收取方式是按贷款本金计算
                calcAmnt = loanAmount;
            } else if (TransBizConstant.FEE_CALC_METHOD_2.equals(calcMethod)) {//费用收取方式是按每期本金计算
                if (i == tenor) {
                    calcAmnt = loanAmount;
                }
            } else if (TransBizConstant.FEE_CALC_METHOD_0.equals(calcMethod)) {//费用收取方式是按剩余本经
                calcAmnt = loanAmount;
            }
            monthlyFee = getFeeAmnt(i, calcAmnt, loanPlanParamBean);

            //计算本金和利息
            if (i == 1) {//首期（由于首期中天数存在跨月的情况，分离开来）
                BigDecimal firstMonthlyInterest;
                if (waiveInterestDays > 0) {
                    int days = DateUtils.getDaysBetween(loanPlanParamBean.getBusinessDate(), firstPaymentDate);
                    intrDays = Math.max(days - waiveInterestDays, 0);
                    firstMonthlyInterest = loanAmount.multiply(loanPlanParamBean.getDailyRate()).multiply(BigDecimal.valueOf(intrDays)).setScale(dcimPlace, carryCondInt);
                } else {
                    firstMonthlyInterest = getMonthInterest(loanPlanParamBean, monthlyInterest, loanAmount, i, firstPaymentDate, loanPlanParamBean.getBusinessDate(), firstPaymentDate, loanPlanParamBean.getDailyRate(), dcimPlace, carryCondInt);
                }
                if (i == tenor) {//如果仅贷款一期，需要设置剩余本金
                    //当期总金额 = 贷款+月利息+月费用
                    repaymentPlanInfo.setTotalAmount(loanAmount.add(firstMonthlyInterest).add(monthlyFee).setScale(dcimPlace,
                            carryCondInt));
                    repaymentPlanInfo.setPrincipalAmount(loanAmount);//本金=总余额
                } else {//贷多期
                    //当期总金额=利息+月费用
                    repaymentPlanInfo.setTotalAmount(firstMonthlyInterest.add(monthlyFee).setScale(dcimPlace, carryCondInt));
                    repaymentPlanInfo.setPrincipalAmount(BigDecimal.ZERO);//本金是0
                }
                repaymentPlanInfo.setInterestAmount(firstMonthlyInterest);//利息
            } else {
                Date thisPaymentDueDate = getCurrentPaymentDueDate(firstPaymentDate, i);
                Date lastPaymentDueDate = getPaymentDueDate(list, i - 1);
                if (waiveInterestDays > 0) {
                    //贴息
                    int days = DateUtils.getDaysBetween(thisPaymentDueDate, lastPaymentDueDate);
                    intrDays = Math.max(days - waiveInterestDays, 0);
                    BigDecimal interest = loanAmount.multiply(loanPlanParamBean.getDailyRate()).multiply(BigDecimal.valueOf(intrDays)).setScale(dcimPlace, carryCondInt);
                    repaymentPlanInfo.setInterestAmount(interest);//利息
                } else {
                    //还款计划改造
                    BigDecimal interest = getMonthInterest(loanPlanParamBean, monthlyInterest, loanAmount, i, firstPaymentDate, lastPaymentDueDate, thisPaymentDueDate, loanPlanParamBean.getDailyRate(), dcimPlace, carryCondInt);
                    repaymentPlanInfo.setInterestAmount(interest);//利息
                }

                if (i == tenor) {//尾期（尾期中需要还本金，分离开来）
                    repaymentPlanInfo.setTotalAmount(loanAmount.add(repaymentPlanInfo.getInterestAmount()).add(monthlyFee).setScale(dcimPlace, carryCondInt));//总金额
                    repaymentPlanInfo.setPrincipalAmount(loanAmount);//本金
                } else {
                    repaymentPlanInfo.setTotalAmount(repaymentPlanInfo.getInterestAmount().add(monthlyFee).setScale(dcimPlace, carryCondInt));
                    repaymentPlanInfo.setPrincipalAmount(BigDecimal.ZERO);//本金
                }

            }

            repaymentPlanInfo.setFeeAmount(monthlyFee);//费用
            repaymentPlanInfo.setBillingTenor(i);//期数
            Date intrStartDate;
            if (i == 1) {
                repaymentPlanInfo.setPaymentDueDate(firstPaymentDate);
                intrStartDate = DateUtils.getDate(loanPlanParamBean.getPostingDate(), loanPlanParamBean.getWaiveInterestDays());
            } else {
                repaymentPlanInfo.setPaymentDueDate(getCurrentPaymentDueDate(firstPaymentDate, i));//还款日期
                intrStartDate = DateUtils.getDate(getPaymentDueDate(list, i - 1), loanPlanParamBean.getWaiveInterestDays());
            }

            if (waiveInterestDays > 0 && intrDays == 0) {
                intrStartDate = repaymentPlanInfo.getPaymentDueDate();
            }
            repaymentPlanInfo.setIntrStartDate(intrStartDate);

            list.add(repaymentPlanInfo);
        }
        return list;
    }


    /**
     * 利随本清，随借随还公用还款计划
     *
     * @param loanPlanParamBean
     * @return
     */
    private List<AmRepaymentPlanInfo> computeInterestPayEnd(LoanPlanParamBean loanPlanParamBean) throws TxnBizException {


        BigDecimal dayRate = loanPlanParamBean.getDailyRate();
        Integer dcimPlace = loanPlanParamBean.getAmortMethod().getDcimPlace(); //小数位
        Integer carryCondInt = loanPlanParamBean.getCarryCondInt();//进位条件

        Date txnPostingDate = loanPlanParamBean.getPostingDate();//贷款时间

        AmRepaymentPlanInfo amRepaymentPlanInfo = buildRepaymentPlanInfo(loanPlanParamBean);
        Date loanDueDate;
        if (loanPlanParamBean.getFirstPaymentDay() != null) {
            loanDueDate = loanPlanParamBean.getFirstPaymentDay();
        } else {
            Integer loanDurationVal = loanPlanParamBean.getPrProductTable().getLoanDurationValue();//贷款时长
            String loanDuration = loanPlanParamBean.getPrProductTable().getLoanDurationUnit();//产品期数单位
            if (loanDurationVal == null) {
                logger.error(TxnRespCode.LOAN_PRODUCT_LOAN_TIME_NONE.getMsg());
                throw new TxnBizException(TxnRespCode.LOAN_PRODUCT_LOAN_TIME_NONE.getCode(),
                        TxnRespCode.LOAN_PRODUCT_LOAN_TIME_NONE.getUserTips());
            }
            if (TransBizConstant.LOAN_DURATION_MONTH.equals(loanDuration)) {//月
                loanDueDate = DateUtils.getDateByMonth(txnPostingDate, loanDurationVal);
            } else if (TransBizConstant.LOAN_DURATION_WEEK.equals(loanDuration)) {//周
                loanDueDate = DateUtils.getDate(txnPostingDate, loanDurationVal * 7);
            } else {//天
                loanDueDate = DateUtils.getDate(txnPostingDate, loanDurationVal);
            }
        }

        //起息日
        Date intrStartDate = DateUtils.getDate(txnPostingDate, loanPlanParamBean.getWaiveInterestDays());
        int diffDays = DateUtils.getDaysBetween(txnPostingDate, loanDueDate);

        int waiveInterestDays = loanPlanParamBean.getWaiveInterestDays();
        int intrDays = Math.max(diffDays - waiveInterestDays, 0);
        BigDecimal interest;//利息
        interest =
                loanPlanParamBean.getLoanAmount().multiply(dayRate).multiply(BigDecimal.valueOf(intrDays)).setScale(dcimPlace, carryCondInt);
        if (waiveInterestDays > 0 && intrDays == 0) {
            intrStartDate = loanDueDate;
        }
        amRepaymentPlanInfo.setIntrStartDate(intrStartDate);


        amRepaymentPlanInfo.setPaymentDueDate(loanDueDate);

        //费用
        BigDecimal fee = getFeeAmnt(TransBizConstant.ONE, loanPlanParamBean.getLoanAmount(), loanPlanParamBean);

        amRepaymentPlanInfo.setPrincipalAmount(loanPlanParamBean.getLoanAmount());//本金
        amRepaymentPlanInfo.setInterestAmount(interest);//利息
        amRepaymentPlanInfo.setFeeAmount(fee);//费用
        amRepaymentPlanInfo.setTotalAmount(amRepaymentPlanInfo.getPrincipalAmount().add(interest).add(fee).setScale(dcimPlace,
                carryCondInt));//总金额
        amRepaymentPlanInfo.setBillingTenor(1);//期数

        List<AmRepaymentPlanInfo> results = new ArrayList<>();
        results.add(amRepaymentPlanInfo);

        return results;
    }


    /**
     * 获取本期费用金额
     *
     * @param billingTenor      期数
     * @param calAmnt           每月费用
     * @param loanPlanParamBean 还款计划参数
     * @return java.math.BigDecimal
     * @author : zhenghaopu
     * date: 2019/9/25
     */
    private BigDecimal getFeeAmnt(Integer billingTenor, BigDecimal calAmnt,
                                  LoanPlanParamBean loanPlanParamBean) {
        Integer loanTenor = loanPlanParamBean.getTenor();//期数
        String feeType = loanPlanParamBean.getInstallmentFeeTable().getChargeFrequency();//费用收取频率
        BigDecimal feeRate = loanPlanParamBean.getInstallmentFeeTable().getChargePercent();////费率
        String chargeFixedOption = loanPlanParamBean.getInstallmentFeeTable().getChargeOption();//费用收取方式
        BigDecimal chargeMaxAmnt = loanPlanParamBean.getInstallmentFeeTable().getMaxAmount();//最大费用
        BigDecimal chargeMinAmnt = loanPlanParamBean.getInstallmentFeeTable().getMinAmount();//最小值
        Integer dcimPlace = loanPlanParamBean.getAmortMethod().getDcimPlace();//小数位
        Integer carryCondInt = loanPlanParamBean.getCarryCondInt();//进位条件

        BigDecimal feeAmt = BigDecimal.ZERO;
        if (calAmnt.compareTo(BigDecimal.ZERO) == 0) {
            return feeAmt;
        }

        if (TransBizConstant.FEE_CHARGE_FIXED_OPTION_NONE.equals(chargeFixedOption)) {
            if (TransBizConstant.FEE_CHARGE_OPTION_AVERAGE.equals(feeType)) {//每期平均收取
                feeAmt = calAmnt.multiply(feeRate);//每期金额
            } else if (TransBizConstant.FEE_CHARGE_OPTION_FIRST_ALL.equals(feeType)) {//首期全部收取（reserved）
                if (billingTenor == 1) {
                    feeAmt = calAmnt.multiply(feeRate);
                }
            } else if (TransBizConstant.FEE_CHARGE_OPTION_END_ALL.equals(feeType)) { //尾期全部收取
                if (billingTenor.equals(loanTenor)) {
                    feeAmt = calAmnt.multiply(feeRate);
                }
            }
            //与最大最小费用金额进行比较
            if (chargeMaxAmnt != null && feeAmt.compareTo(chargeMaxAmnt) > 0) {
                feeAmt = chargeMaxAmnt;
            } else if (chargeMinAmnt != null && feeAmt.compareTo(chargeMinAmnt) < 0) {
                feeAmt = chargeMinAmnt;
            }
        } else {
            if (TransBizConstant.FEE_CHARGE_OPTION_AVERAGE.equals(feeType)) {//每期平均收取
                feeAmt = loanPlanParamBean.getInstallmentFeeTable().getFixedAmount();//每期金额
            } else if (TransBizConstant.FEE_CHARGE_OPTION_FIRST_ALL.equals(feeType)) {//首期全部收取（reserved）
                if (billingTenor == 1) {
                    feeAmt = loanPlanParamBean.getInstallmentFeeTable().getFixedAmount();
                }
            } else if (TransBizConstant.FEE_CHARGE_OPTION_END_ALL.equals(feeType)) { //尾期全部收取
                if (billingTenor.equals(loanTenor)) {
                    feeAmt = loanPlanParamBean.getInstallmentFeeTable().getFixedAmount();
                }
            }
        }

        return feeAmt.setScale(dcimPlace, carryCondInt);
    }

    /**
     * 计算首期还款日
     *
     * @param loanDate 业务日期
     * @param cycleDay 还款日
     * @param limitDay 间隔时间
     * @return 首期还款日
     */
    @Override
    public Date computeFirstPaymentDate(Date loanDate, Integer cycleDay, Integer limitDay) {
        GregorianCalendar gcEndDate = new GregorianCalendar();
        gcEndDate.setTime(loanDate);
        gcEndDate.set(GregorianCalendar.DAY_OF_MONTH, cycleDay);
        GregorianCalendar gcStartDate = new GregorianCalendar();
        gcStartDate.setTime(loanDate);
        //业务日期计算的号大于还款日
        if (gcStartDate.get(GregorianCalendar.DAY_OF_MONTH) > cycleDay) {
            GregorianCalendar gcTmpDate = new GregorianCalendar();
            gcTmpDate.setTime(loanDate);
            gcTmpDate.set(GregorianCalendar.DAY_OF_MONTH, cycleDay);
            //将临时还款日向后调整一个月
            gcTmpDate.add(GregorianCalendar.MONTH, 1);
            //计算当前时间到临时还款日时间差
            Integer diffDays = DateUtils.getDaysBetween(loanDate, gcTmpDate.getTime());
            //时间差大等于于间隔日期 实际还款日向后调整一个月
            if (diffDays >= limitDay) {
                gcEndDate.add(GregorianCalendar.MONTH, 1);
            } else {//时间差小于间隔日期 实际还款日向后调整俩个月  如月末特殊情况
                gcEndDate.add(GregorianCalendar.MONTH, 2);
            }
        } else {//业务日期计算的号小于还款日
            GregorianCalendar gcTmpDate = new GregorianCalendar();
            gcTmpDate.setTime(loanDate);
            gcTmpDate.set(GregorianCalendar.DAY_OF_MONTH, cycleDay);
            //当前时间到还款日时间差
            Integer diffDays = DateUtils.getDaysBetween(loanDate, gcTmpDate.getTime());
            //时间差小于间隔日期 还款日向后调整一个月
            if (diffDays < limitDay) {
                gcEndDate.add(GregorianCalendar.MONTH, 1);
            }
        }
        return gcEndDate.getTime();
    }


    @Override
    public List<AmWaiveRepaymentPlanInfo> buildWaiveRepaymentListByCustRightOrPromotion(List<AmRepaymentPlanInfo> amRepaymentPlanInfoList, LoanPlanParamBean loanPlanParamBean) throws TxnBizException {
        CmCustomerRightInfo rightInfo = loanPlanParamBean.getRightInfo();
        PrPromotionInfo prPromotionInfo = loanPlanParamBean.getPrPromotionInfo();
        List<AmWaiveRepaymentPlanInfo> waiveRepaymentPlanInfoList = this.buildWaiveRepaymentList(amRepaymentPlanInfoList, loanPlanParamBean);
        for (AmWaiveRepaymentPlanInfo waivePlan : waiveRepaymentPlanInfoList) {
            BigDecimal waiveIntr = amRepaymentPlanInfoList.stream().filter(plan -> waivePlan.getBillingTenor().equals(plan.getBillingTenor())).findFirst().get().getWaivedAmount();
            waivePlan.setInterestAmount(waiveIntr);
            waivePlan.setTotalAmount(waiveIntr);
            if (rightInfo != null && CustomerConstant.RIGHTS_TYPE_02.equals(rightInfo.getRightsType())) {
                //客户权益  按折扣免息
                waivePlan.setActualDailyRate(loanPlanParamBean.getDailyRate().multiply(BigDecimal.ONE.subtract(rightInfo.getDiscountRate())).setScale(7, BigDecimal.ROUND_HALF_UP));
            } else if (prPromotionInfo != null) {
                //促销活动
                //1 = 利率打折
                if (TransBizConstant.PROMOTION_RATE_FLAG_1.equals(prPromotionInfo.getRateFlag())) {
                    waivePlan.setActualDailyRate(loanPlanParamBean.getDailyRate().multiply(BigDecimal.ONE.subtract(prPromotionInfo.getRateDiscountPercentage())).setScale(7, BigDecimal.ROUND_HALF_UP));
                }
                //2 = 固定利率
                else if (TransBizConstant.PROMOTION_RATE_FLAG_2.equals(prPromotionInfo.getRateFlag())) {
                    BigDecimal dailyRate = loanPlanParamBean.getOriginalDailyRate().subtract(loanPlanParamBean.getDailyRate());
                    if (dailyRate.compareTo(BigDecimal.ZERO) <= 0) {
                        dailyRate = loanPlanParamBean.getDailyRate();
                    }
                    waivePlan.setActualDailyRate(dailyRate);
                }
            }
        }
        return waiveRepaymentPlanInfoList;
    }

    @Override
    public List<AmWaiveRepaymentPlanInfo> buildWaiveRepaymentListByProduct(List<AmRepaymentPlanInfo> amRepaymentPlanInfoList, LoanPlanParamBean loanPlanParamBean) throws TxnBizException {
        List<AmWaiveRepaymentPlanInfo> waiveRepaymentPlanInfoList = null;

        //还款方式 来源还款方式表
        String paymentMethod = loanPlanParamBean.getAmortMethod().getChargeOption();
        //进位条件
        String carryCond = loanPlanParamBean.getAmortMethod().getCarryCond();
        //根据还款方式进位条件获取 BigDecimal 中的进位计算值
        if (TransBizConstant.CARRY_COND_1.equals(carryCond)) { //四舍五入
            loanPlanParamBean.setCarryCondInt(BigDecimal.ROUND_HALF_UP);
        }
        if (TransBizConstant.CARRY_COND_2.equals(carryCond)) {//直接进位
            loanPlanParamBean.setCarryCondInt(BigDecimal.ROUND_UP);
        }
        if (TransBizConstant.CARRY_COND_3.equals(carryCond)) {//截位
            loanPlanParamBean.setCarryCondInt(BigDecimal.ROUND_DOWN);
        }

        loanPlanParamBean.setWaiveYearRate(waiveInterestTableService.getYearRate(loanPlanParamBean.getPrWaiveInterestTable()));//年利率


        //分期付款（等本等息）
        if (TransBizConstant.PRIN_CHARGE_OPTION_EPEI.equals(paymentMethod)) {
            waiveRepaymentPlanInfoList = computeWaiveEPEI(amRepaymentPlanInfoList, loanPlanParamBean);
        }
        //等额本息
        if (TransBizConstant.PRIN_CHARGE_OPTION_EPI.equals(paymentMethod)) {
            waiveRepaymentPlanInfoList = computeWaiveEPI(amRepaymentPlanInfoList, loanPlanParamBean);
        }
        //等额本金
        else if (TransBizConstant.PRIN_CHARGE_OPTION_EP.equals(paymentMethod)) {
            waiveRepaymentPlanInfoList = computeWaiveEP(amRepaymentPlanInfoList, loanPlanParamBean);
        }
        //按期付息，到期还本付息
        else if (TransBizConstant.PRIN_CHARGE_OPTION_MONTHLY_INTPAY.equals(paymentMethod)) {
            waiveRepaymentPlanInfoList = computeWaiveMonthlyInterestPayEnd(amRepaymentPlanInfoList, loanPlanParamBean);
        }
        //按月付息，按年还本付息
        else if (TransBizConstant.PRIN_CHARGE_OPTION_MI_YP.equals(paymentMethod)) {
            waiveRepaymentPlanInfoList = computeWaiveMonthlyInterestYearPay(amRepaymentPlanInfoList, loanPlanParamBean);
        }
        //随见随还 利随本清
        else if (TransBizConstant.PRIN_CHARGE_OPTION_INTPAY.equals(paymentMethod)) {
            waiveRepaymentPlanInfoList = computeWaiveInterestPayEnd(amRepaymentPlanInfoList, loanPlanParamBean);
        }
        return waiveRepaymentPlanInfoList;
    }

    /**
     * 随见随还 利随本清
     *
     * @param amRepaymentPlanInfoList
     * @param loanPlanParamBean
     * @return
     */
    private List<AmWaiveRepaymentPlanInfo> computeWaiveInterestPayEnd(List<AmRepaymentPlanInfo> amRepaymentPlanInfoList, LoanPlanParamBean loanPlanParamBean) throws TxnBizException {
        List<AmWaiveRepaymentPlanInfo> waiveRepaymentPlanInfoList = this.buildWaiveRepaymentList(amRepaymentPlanInfoList, loanPlanParamBean);

        BigDecimal waiveDailyRate = loanPlanParamBean.getWaiveDailyRate();
        Integer carryCondInt = loanPlanParamBean.getCarryCondInt();
        Integer dcimPlace = loanPlanParamBean.getAmortMethod().getDcimPlace();//小数位
        Integer waiveInterestDays = loanPlanParamBean.getWaiveInterestDays();
        for (AmWaiveRepaymentPlanInfo amWaiveRepaymentPlanInfo : waiveRepaymentPlanInfoList) {

            Date lastPaymentDueDate = loanPlanParamBean.getPostingDate();
            if (amWaiveRepaymentPlanInfo.getBillingTenor() != 1) {
                lastPaymentDueDate = this.getPaymentDueDate(amRepaymentPlanInfoList, amWaiveRepaymentPlanInfo.getBillingTenor() - 1);
            }
            Date thisPaymentDueDate = this.getPaymentDueDate(amRepaymentPlanInfoList, amWaiveRepaymentPlanInfo.getBillingTenor());
            int days = DateUtils.getDaysBetween(lastPaymentDueDate, thisPaymentDueDate);
            waiveInterestDays = Math.min(waiveInterestDays, days);

            BigDecimal interest = loanPlanParamBean.getLoanAmount().multiply(waiveDailyRate).multiply(BigDecimal.valueOf(waiveInterestDays)).setScale(dcimPlace, carryCondInt);
            amWaiveRepaymentPlanInfo.setInterestAmount(interest);
            amWaiveRepaymentPlanInfo.setTotalAmount(interest);
        }
        return waiveRepaymentPlanInfoList;
    }

    /**
     * 按月付息，按年还本付息
     *
     * @param amRepaymentPlanInfoList
     * @param loanPlanParamBean
     * @return
     * @throws TxnBizException
     */
    private List<AmWaiveRepaymentPlanInfo> computeWaiveMonthlyInterestYearPay(List<AmRepaymentPlanInfo> amRepaymentPlanInfoList, LoanPlanParamBean loanPlanParamBean) throws TxnBizException {
        List<AmWaiveRepaymentPlanInfo> waiveRepaymentPlanInfoList = this.buildWaiveRepaymentList(amRepaymentPlanInfoList, loanPlanParamBean);

        BigDecimal waiveDailyRate = loanPlanParamBean.getWaiveDailyRate();
        Integer carryCondInt = loanPlanParamBean.getCarryCondInt();
        Integer dcimPlace = loanPlanParamBean.getAmortMethod().getDcimPlace();//小数位
        Integer waiveInterestDays = loanPlanParamBean.getWaiveInterestDays();
        BigDecimal remainAmt = loanPlanParamBean.getLoanAmount();
        BigDecimal calAmt = loanPlanParamBean.getLoanAmount();

        int tenor = loanPlanParamBean.getTenor();


        for (AmWaiveRepaymentPlanInfo amWaiveRepaymentPlanInfo : waiveRepaymentPlanInfoList) {
            Date lastPaymentDueDate = loanPlanParamBean.getPostingDate();
            if (amWaiveRepaymentPlanInfo.getBillingTenor() != 1) {
                lastPaymentDueDate = this.getPaymentDueDate(amRepaymentPlanInfoList, amWaiveRepaymentPlanInfo.getBillingTenor() - 1);
            }
            Date thisPaymentDueDate = this.getPaymentDueDate(amRepaymentPlanInfoList, amWaiveRepaymentPlanInfo.getBillingTenor());
            int days = DateUtils.getDaysBetween(lastPaymentDueDate, thisPaymentDueDate);
            waiveInterestDays = Math.min(waiveInterestDays, days);
            BigDecimal interest = calAmt.multiply(waiveDailyRate).multiply(BigDecimal.valueOf(waiveInterestDays)).setScale(dcimPlace, carryCondInt);

            amWaiveRepaymentPlanInfo.setInterestAmount(interest);
            amWaiveRepaymentPlanInfo.setTotalAmount(interest);
            BigDecimal thisPrincipalAmt = amRepaymentPlanInfoList.stream().filter(plan -> amWaiveRepaymentPlanInfo.getBillingTenor().equals(plan.getBillingTenor())).findFirst().get().getPrincipalAmount();
            remainAmt = remainAmt.subtract(thisPrincipalAmt);
            if ((amWaiveRepaymentPlanInfo.getBillingTenor()) % TransBizConstant.MONTH == 0 || amWaiveRepaymentPlanInfo.getBillingTenor() == tenor) {
                calAmt = remainAmt;
            }
        }
        return waiveRepaymentPlanInfoList;
    }

    /**
     * 按期付息，到期还本付息
     *
     * @param amRepaymentPlanInfoList
     * @param loanPlanParamBean
     * @return
     * @throws TxnBizException
     */
    private List<AmWaiveRepaymentPlanInfo> computeWaiveMonthlyInterestPayEnd(List<AmRepaymentPlanInfo> amRepaymentPlanInfoList, LoanPlanParamBean loanPlanParamBean) throws TxnBizException {

        List<AmWaiveRepaymentPlanInfo> waiveRepaymentPlanInfoList = this.buildWaiveRepaymentList(amRepaymentPlanInfoList, loanPlanParamBean);

        BigDecimal waiveDailyRate = loanPlanParamBean.getWaiveDailyRate();
        Integer carryCondInt = loanPlanParamBean.getCarryCondInt();
        Integer dcimPlace = loanPlanParamBean.getAmortMethod().getDcimPlace();//小数位
        Integer waiveInterestDays = loanPlanParamBean.getWaiveInterestDays();

        for (AmWaiveRepaymentPlanInfo amWaiveRepaymentPlanInfo : waiveRepaymentPlanInfoList) {
            Date lastPaymentDueDate = loanPlanParamBean.getPostingDate();
            if (amWaiveRepaymentPlanInfo.getBillingTenor() != 1) {
                lastPaymentDueDate = this.getPaymentDueDate(amRepaymentPlanInfoList, amWaiveRepaymentPlanInfo.getBillingTenor() - 1);
            }
            Date thisPaymentDueDate = this.getPaymentDueDate(amRepaymentPlanInfoList, amWaiveRepaymentPlanInfo.getBillingTenor());
            int days = DateUtils.getDaysBetween(lastPaymentDueDate, thisPaymentDueDate);
            waiveInterestDays = Math.min(waiveInterestDays, days);

            BigDecimal interest = loanPlanParamBean.getLoanAmount().multiply(waiveDailyRate).multiply(BigDecimal.valueOf(waiveInterestDays)).setScale(dcimPlace, carryCondInt);

            amWaiveRepaymentPlanInfo.setInterestAmount(interest);
            amWaiveRepaymentPlanInfo.setTotalAmount(interest);
        }
        return waiveRepaymentPlanInfoList;
    }

    /**
     * 等额本金
     *
     * @param amRepaymentPlanInfoList
     * @param loanPlanParamBean
     * @return
     */
    private List<AmWaiveRepaymentPlanInfo> computeWaiveEP(List<AmRepaymentPlanInfo> amRepaymentPlanInfoList, LoanPlanParamBean loanPlanParamBean) throws TxnBizException {
        List<AmWaiveRepaymentPlanInfo> waiveRepaymentPlanInfoList = this.buildWaiveRepaymentList(amRepaymentPlanInfoList, loanPlanParamBean);

        BigDecimal waiveDailyRate = loanPlanParamBean.getWaiveDailyRate();
        Integer carryCondInt = loanPlanParamBean.getCarryCondInt();
        Integer dcimPlace = loanPlanParamBean.getAmortMethod().getDcimPlace();//小数位
        Integer waiveInterestDays = loanPlanParamBean.getWaiveInterestDays();

        BigDecimal remainAmt = loanPlanParamBean.getLoanAmount();
        for (AmWaiveRepaymentPlanInfo amWaiveRepaymentPlanInfo : waiveRepaymentPlanInfoList) {
            Date lastPaymentDueDate = loanPlanParamBean.getPostingDate();
            if (amWaiveRepaymentPlanInfo.getBillingTenor() != 1) {
                lastPaymentDueDate = this.getPaymentDueDate(amRepaymentPlanInfoList, amWaiveRepaymentPlanInfo.getBillingTenor() - 1);
            }
            Date thisPaymentDueDate = this.getPaymentDueDate(amRepaymentPlanInfoList, amWaiveRepaymentPlanInfo.getBillingTenor());
            int days = DateUtils.getDaysBetween(lastPaymentDueDate, thisPaymentDueDate);
            waiveInterestDays = Math.min(waiveInterestDays, days);

            BigDecimal interest = remainAmt.multiply(waiveDailyRate).multiply(BigDecimal.valueOf(waiveInterestDays)).setScale(dcimPlace, carryCondInt);
            ;
            amWaiveRepaymentPlanInfo.setInterestAmount(interest);
            amWaiveRepaymentPlanInfo.setTotalAmount(interest);
            BigDecimal thisPrincipalAmt = amRepaymentPlanInfoList.stream().filter(plan -> amWaiveRepaymentPlanInfo.getBillingTenor().equals(plan.getBillingTenor())).findFirst().get().getPrincipalAmount();
            remainAmt = remainAmt.subtract(thisPrincipalAmt);
        }

        return waiveRepaymentPlanInfoList;
    }

    /**
     * 等额本息
     *
     * @param amRepaymentPlanInfoList
     * @param loanPlanParamBean
     * @return
     * @throws TxnBizException
     */
    private List<AmWaiveRepaymentPlanInfo> computeWaiveEPI(List<AmRepaymentPlanInfo> amRepaymentPlanInfoList, LoanPlanParamBean loanPlanParamBean) throws TxnBizException {
        List<AmWaiveRepaymentPlanInfo> waiveRepaymentPlanInfoList = this.buildWaiveRepaymentList(amRepaymentPlanInfoList, loanPlanParamBean);

        BigDecimal waiveDailyRate = loanPlanParamBean.getWaiveDailyRate();
        Integer carryCondInt = loanPlanParamBean.getCarryCondInt();
        Integer dcimPlace = loanPlanParamBean.getAmortMethod().getDcimPlace();//小数位
        Integer waiveInterestDays = loanPlanParamBean.getWaiveInterestDays();

        BigDecimal remainAmt = amRepaymentPlanInfoList.stream().map(AmRepaymentPlanInfo::getPrincipalAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        for (AmWaiveRepaymentPlanInfo amWaiveRepaymentPlanInfo : waiveRepaymentPlanInfoList) {
            Date lastPaymentDueDate = loanPlanParamBean.getPostingDate();
            if (amWaiveRepaymentPlanInfo.getBillingTenor() != 1) {
                lastPaymentDueDate = this.getPaymentDueDate(amRepaymentPlanInfoList, amWaiveRepaymentPlanInfo.getBillingTenor() - 1);
            }
            Date thisPaymentDueDate = this.getPaymentDueDate(amRepaymentPlanInfoList, amWaiveRepaymentPlanInfo.getBillingTenor());
            int days = DateUtils.getDaysBetween(lastPaymentDueDate, thisPaymentDueDate);
            waiveInterestDays = Math.min(waiveInterestDays, days);

            BigDecimal interest = remainAmt.multiply(waiveDailyRate).multiply(BigDecimal.valueOf(waiveInterestDays)).setScale(dcimPlace, carryCondInt);
            amWaiveRepaymentPlanInfo.setInterestAmount(interest);
            amWaiveRepaymentPlanInfo.setTotalAmount(interest);
            BigDecimal thisPrincipalAmt = amRepaymentPlanInfoList.stream().filter(plan -> amWaiveRepaymentPlanInfo.getBillingTenor().equals(plan.getBillingTenor())).findFirst().get().getPrincipalAmount();
            remainAmt = remainAmt.subtract(thisPrincipalAmt);
        }

        return waiveRepaymentPlanInfoList;
    }

    /**
     * 等本等息
     *
     * @param amRepaymentPlanInfoList
     * @param loanPlanParamBean
     * @return
     * @throws TxnBizException
     */
    private List<AmWaiveRepaymentPlanInfo> computeWaiveEPEI(List<AmRepaymentPlanInfo> amRepaymentPlanInfoList, LoanPlanParamBean loanPlanParamBean) throws TxnBizException {
        List<AmWaiveRepaymentPlanInfo> waiveRepaymentPlanInfoList = this.buildWaiveRepaymentList(amRepaymentPlanInfoList, loanPlanParamBean);

        Integer carryCondInt = loanPlanParamBean.getCarryCondInt();
        Integer dcimPlace = loanPlanParamBean.getAmortMethod().getDcimPlace();//小数位
        Integer waiveInterestDays = loanPlanParamBean.getWaiveInterestDays();
        Integer tenor = loanPlanParamBean.getTenor();
        Date businessDate = loanPlanParamBean.getBusinessDate();


        BigDecimal monthRate =
                loanPlanParamBean.getWaiveYearRate().multiply(BigDecimal.valueOf(1L + tenor)).divide(BigDecimal.valueOf(24L * tenor), 7,
                        BigDecimal.ROUND_HALF_UP);
        PrWaiveInterestTable prWaiveInterestTable = loanPlanParamBean.getPrWaiveInterestTable();
        int monthBase = interestTableService.getMonthBase(prWaiveInterestTable.getMonthBase(), businessDate);
        //日利率
        BigDecimal waiveDailyRate = monthRate.divide(
                BigDecimal.valueOf(monthBase), TransBizConstant.BIGDECIMAL_SCALE_7, BigDecimal.ROUND_HALF_UP);


        for (AmWaiveRepaymentPlanInfo amWaiveRepaymentPlanInfo : waiveRepaymentPlanInfoList) {
            Date lastPaymentDueDate = loanPlanParamBean.getPostingDate();
            if (amWaiveRepaymentPlanInfo.getBillingTenor() != 1) {
                lastPaymentDueDate = this.getPaymentDueDate(amRepaymentPlanInfoList, amWaiveRepaymentPlanInfo.getBillingTenor() - 1);
            }
            Date thisPaymentDueDate = this.getPaymentDueDate(amRepaymentPlanInfoList, amWaiveRepaymentPlanInfo.getBillingTenor());
            int days = DateUtils.getDaysBetween(lastPaymentDueDate, thisPaymentDueDate);
            waiveInterestDays = Math.min(waiveInterestDays, days);
            BigDecimal interest = loanPlanParamBean.getLoanAmount().multiply(waiveDailyRate).multiply(BigDecimal.valueOf(waiveInterestDays)).setScale(dcimPlace, carryCondInt);
            amWaiveRepaymentPlanInfo.setInterestAmount(interest);
            amWaiveRepaymentPlanInfo.setTotalAmount(interest);
        }
        return waiveRepaymentPlanInfoList;
    }

    /**
     * 构建贴息还款计划
     *
     * @param amRepaymentPlanInfoList
     * @param loanPlanParamBean
     * @return
     * @throws TxnBizException
     */
    private List<AmWaiveRepaymentPlanInfo> buildWaiveRepaymentList(List<AmRepaymentPlanInfo> amRepaymentPlanInfoList, LoanPlanParamBean loanPlanParamBean) throws TxnBizException {
        Date businessDate = loanPlanParamBean.getBusinessDate();
        String liabilityFlag = "";
        BigDecimal actualDailyRate = loanPlanParamBean.getDailyRate();
        if (null != loanPlanParamBean.getPrWaiveInterestTable()) {
            liabilityFlag = loanPlanParamBean.getPrWaiveInterestTable().getInterestDiscountPayer();
            actualDailyRate = loanPlanParamBean.getWaiveDailyRate();
        } else if (null != loanPlanParamBean.getRightInfo()) {
            liabilityFlag = loanPlanParamBean.getRightInfo().getInterestDiscountPayer();
        } else if (null != loanPlanParamBean.getPrPromotionInfo()) {
            liabilityFlag = loanPlanParamBean.getPrPromotionInfo().getInterestDiscountPayer();
        }
        List<AmWaiveRepaymentPlanInfo> waiveRepaymentPlanInfoList = new ArrayList<>(amRepaymentPlanInfoList.size());
        for (AmRepaymentPlanInfo amRepaymentPlanInfo : amRepaymentPlanInfoList) {
            AmWaiveRepaymentPlanInfo amWaiveRepaymentPlanInfo = new AmWaiveRepaymentPlanInfo();
            amWaiveRepaymentPlanInfo.setPrincipalAmount(BigDecimal.ZERO);
            amWaiveRepaymentPlanInfo.setFeeAmount(BigDecimal.ZERO);
            amWaiveRepaymentPlanInfo.setRepayPrincipal(BigDecimal.ZERO);//当期已还本金
            amWaiveRepaymentPlanInfo.setRepayInterest(BigDecimal.ZERO);
            amWaiveRepaymentPlanInfo.setRepayFee(BigDecimal.ZERO);//当期已还费用
            amWaiveRepaymentPlanInfo.setRecordVersionNumber(TransBizConstant.INIT_RECORD_VERSION_NUMBER);//版本号
            amWaiveRepaymentPlanInfo.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_0);//状态 0 未抛账
            try {
                amWaiveRepaymentPlanInfo.setId(segmentService.getId(TransBizConstant.AM_WAIVE_REPAYMENT_PLAN_ID));
            } catch (SegmentException e) {
                logger.error("主键生成错误", e);
                throw new TxnBizException(TxnRespCode.ERROR.getUserTips());
            }
            amWaiveRepaymentPlanInfo.setLoanId(amRepaymentPlanInfo.getLoanId());//订单id
            amWaiveRepaymentPlanInfo.setCustomerId(amRepaymentPlanInfo.getCustomerId());//客户id
            amWaiveRepaymentPlanInfo.setTenantId(amRepaymentPlanInfo.getTenantId());//租户id
            amWaiveRepaymentPlanInfo.setOrganizationId(amRepaymentPlanInfo.getOrganizationId());//机构id
            amWaiveRepaymentPlanInfo.setChannel(amRepaymentPlanInfo.getChannel());//渠道
            amWaiveRepaymentPlanInfo.setProductId(amRepaymentPlanInfo.getProductId());//产品id
            amWaiveRepaymentPlanInfo.setUpdateBy(amRepaymentPlanInfo.getCustomerId());
            amWaiveRepaymentPlanInfo.setBillingTenor(amRepaymentPlanInfo.getBillingTenor());
            amWaiveRepaymentPlanInfo.setRecordVersionNumber(TransBizConstant.INIT_RECORD_VERSION_NUMBER);
            amWaiveRepaymentPlanInfo.setLiabilityFlag(liabilityFlag);
            amWaiveRepaymentPlanInfo.setPaymentDueDate(amRepaymentPlanInfo.getPaymentDueDate());
            amWaiveRepaymentPlanInfo.setActualDailyRate(actualDailyRate);
            if (amRepaymentPlanInfo.getBillingTenor() == 1) {
                amWaiveRepaymentPlanInfo.setIntrStartDate(businessDate);
            } else {
                amWaiveRepaymentPlanInfo.setIntrStartDate(this.getPaymentDueDate(amRepaymentPlanInfoList, amRepaymentPlanInfo.getBillingTenor() - 1));
            }
            waiveRepaymentPlanInfoList.add(amWaiveRepaymentPlanInfo);
        }
        return waiveRepaymentPlanInfoList;
    }

    /**
     * 生成还款计划
     *
     * @param loanPlanParamBean 还款计划计算入参
     * @return 还款计划信息
     */
    private AmRepaymentPlanInfo buildRepaymentPlanInfo(LoanPlanParamBean loanPlanParamBean) throws TxnBizException {
        AmRepaymentPlanInfo repaymentPlanInfo = new AmRepaymentPlanInfo();

        repaymentPlanInfo.setRepayPrincipal(BigDecimal.ZERO);//当期已还本金
        repaymentPlanInfo.setRepayInterest(BigDecimal.ZERO);//当期已还利息
        repaymentPlanInfo.setRepayFee(BigDecimal.ZERO);//当期已还费用
        repaymentPlanInfo.setRepayPenaltyInterest(BigDecimal.ZERO);//当期已还罚息
        repaymentPlanInfo.setRepayCompoundInterest(BigDecimal.ZERO);//当前已还复利
        repaymentPlanInfo.setWaivedAmount(BigDecimal.ZERO);//当期免除金额
        repaymentPlanInfo.setRecordVersionNumber(TransBizConstant.INIT_RECORD_VERSION_NUMBER);//版本号
        repaymentPlanInfo.setCompensateFlag(TransBizConstant.NO_FLAG);//代偿标识
        repaymentPlanInfo.setBalTransFlag(TransBizConstant.NO_FLAG);//债权转移标识
        repaymentPlanInfo.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_0);//状态 0 未抛账
        repaymentPlanInfo.setDelqDays(TransBizConstant.ZERO);//延滞天数
        try {
            repaymentPlanInfo.setId(segmentService.getId(TransBizConstant.AM_REPAYMENT_PLAN_ID));
        } catch (SegmentException e) {
            logger.error("主键生成错误", e);
            throw new TxnBizException(TxnRespCode.ERROR.getUserTips());
        }
        repaymentPlanInfo.setLoanId(loanPlanParamBean.getLoanId());//订单id
        repaymentPlanInfo.setCustomerId(loanPlanParamBean.getCustomerId());//客户id
        repaymentPlanInfo.setTenantId(loanPlanParamBean.getTenantId());//租户id
        repaymentPlanInfo.setOrganizationId(loanPlanParamBean.getOrganizationId());//机构id
        repaymentPlanInfo.setChannel(loanPlanParamBean.getChannel());//渠道
        repaymentPlanInfo.setProductId(loanPlanParamBean.getProductId());//产品id
        repaymentPlanInfo.setUpdateBy(loanPlanParamBean.getCustomerId());
        repaymentPlanInfo.setRecordVersionNumber(TransBizConstant.INIT_RECORD_VERSION_NUMBER);
        repaymentPlanInfo.setLiabilityFlag(TransBizConstant.LIABILITY_FLAG_00);
        repaymentPlanInfo.setActualDailyRate(loanPlanParamBean.getDailyRate());
        return repaymentPlanInfo;
    }

    private Date getCurrentPaymentDueDate(Date firstPaymentDate, int i) {
        //根据首期计算，如果日期不存在取月最后一天
        Date thisPaymentDueDate = DateUtils.add(firstPaymentDate, GregorianCalendar.MONTH, i - 1);
        return thisPaymentDueDate;
    }

    private BigDecimal getMonthInterest(LoanPlanParamBean loanPlanParamBean, BigDecimal monthIntr, BigDecimal castAmount, int cast_tenor,
                                        Date firstPaymentDate, Date lastPaymentDueDate, Date currPaymentDueDate, BigDecimal dailyRate, Integer dcimPlace, Integer carryCondInt) {
        BigDecimal InterestAmount = BigDecimal.ZERO;
        if (1 == cast_tenor) {
            String firstOption = loanPlanParamBean.getPrProductTable().getFirstPaymentOption();
            //首期
            if (TransBizConstant.FIRST_PAYMENT_OPTION_01.equals(firstOption)) {
                //日对日按月，否则按日
                String firstDate = DateUtils.formatDate(firstPaymentDate);
                String lastDate = DateUtils.formatDate(lastPaymentDueDate);

                String date1 = firstDate.substring(8);
                String date2 = lastDate.substring(8);
                if (date1.equals(date2)) {
                    InterestAmount = monthIntr;
                } else {
                    int days = DateUtils.getDaysBetween(firstPaymentDate, lastPaymentDueDate);
                    InterestAmount = castAmount.multiply(dailyRate).multiply(BigDecimal.valueOf(days)).setScale(dcimPlace, carryCondInt);
                }
            } else if (TransBizConstant.FIRST_PAYMENT_OPTION_02.equals(firstOption)) {
                //按日
                //获取放款日 与 首期还款日间隔天数
                int days = DateUtils.getDaysBetween(firstPaymentDate, lastPaymentDueDate);
                InterestAmount = castAmount.multiply(dailyRate).multiply(BigDecimal.valueOf(days)).setScale(dcimPlace, carryCondInt);
            } else {
                //按月
                InterestAmount = monthIntr;
            }
        } else {
            //非首期
            String planOption = loanPlanParamBean.getPrProductTable().getPlanOption();
            if (TransBizConstant.PLAN_OPTION_01.equals(planOption)) {
                //日对日按月，否则按日
                String firstDate = DateUtils.formatDate(lastPaymentDueDate);
                String currDate = DateUtils.formatDate(currPaymentDueDate);

                String date1 = firstDate.substring(8);
                String date2 = currDate.substring(8);
                if (date1.equals(date2)) {
                    InterestAmount = monthIntr;
                } else {
                    int days = DateUtils.getDaysBetween(currPaymentDueDate, lastPaymentDueDate);
                    InterestAmount = castAmount.multiply(dailyRate).multiply(BigDecimal.valueOf(days)).setScale(dcimPlace, carryCondInt);
                }
            } else if (TransBizConstant.PLAN_OPTION_02.equals(planOption)) {
                //按日
                //获取放款日 与 上期还款日间隔天数
                int days = DateUtils.getDaysBetween(currPaymentDueDate, lastPaymentDueDate);
                InterestAmount = castAmount.multiply(dailyRate).multiply(BigDecimal.valueOf(days)).setScale(dcimPlace, carryCondInt);
            } else {
                //按月
                InterestAmount = monthIntr;
            }
        }
        return InterestAmount;
    }

    @Override
    public List<AmRepaymentPlanInfo> getPlanList(String loanId, String customerId, String tenantId) throws TxnBizException {
        //查询订单还款计划
        List<AmRepaymentPlanInfo> planInfoList = extAmRepaymentPlanInfoMapper.selectByLoanIdAndNotStatus(loanId
                , TransBizConstant.LOAN_PLAN_STATUS_9, customerId, tenantId);
        //是否有还款计划
        if (CollectionUtils.isEmpty(planInfoList)) {
            logger.error("未查询到订单号：{}对应的还款计划", loanId);
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }
        return planInfoList;
    }

    /**
     * 客户续期信息查询
     *
     * @param req
     * @return
     */
    @Override
    public CustomerRenewalRes customerRenewalInfo(CommonAddReq req) throws TxnBizException {
        String userId = req.getUserId();
        String channel = req.getChannel();
        String tenantId = req.getTenantId();
        CmCustomerSecondLevel secondLevel = extCmCustomerSecondLevelMapper.selectByUserIdAndChannel(userId, channel, tenantId);
        if (Objects.isNull(secondLevel)) {
            logger.warn("二级客户信息未找到：{}", userId);
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getMsg());
        }
        String customerId = secondLevel.getCustomerId();
        CmCustomerFirstLevel firstLevel = extCmCustomerFirstLevelMapper.selectFirstLevelByOrgIdAndCusId(customerId, secondLevel.getOrganizationId(), tenantId);
        if (Objects.isNull(firstLevel)) {
            logger.warn("一级客户信息未找到：{}", customerId);
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getMsg());
        }
        CustomerRenewalRes res = new CustomerRenewalRes();
        // 逾期客户不跑风控
        if (StringUtils.isNotBlank(firstLevel.getBlockCode())) {
            res.setTodayOverdueFlag("1");
        }
        res.setSeqNo(req.getSeqNo());
        res.setUserId(userId);
        res.setChannel(channel);
        res.setEcifId(firstLevel.getEcifId());
        res.setTenantId(tenantId);
        List<String> products = new ArrayList<>();
        if (StringUtils.isNotBlank(req.getBizType())) {
            products.addAll(BizTypeProducts.getByBizType(BizTypeEnum.getByCode(req.getBizType())));
        }
        // 查询还款计划
        List<AmRepaymentPlanInfo> planList = extAmRepaymentPlanInfoMapper.queryPlanInfo(tenantId, channel, customerId,
                Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2), products);
        if (CollectionUtils.isNotEmpty(planList)) {
            planList.stream()
                    .filter(plan -> TransBizConstant.LOAN_PLAN_STATUS_2.equalsIgnoreCase(plan.getPlanStatus()))
                    .forEach(plan -> {
                        res.setProductId(plan.getProductId());
                        res.setCurrentOverdueFlag("1");
                    });
        } else {
            AmRepaymentPlanInfo plan = extAmRepaymentPlanInfoMapper.queryMaxPayoffDate(tenantId, channel, customerId,
                    Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_7, TransBizConstant.LOAN_PLAN_STATUS_8, TransBizConstant.LOAN_PLAN_STATUS_9), products);
            String payoffDate = DateUtils.format(plan.getPayoffDate());
            res.setPayoffDate(payoffDate);
        }
        return res;
    }



}
